結果

問題 No.3566 Subsequence Sum
コンテスト
ユーザー pockyny
提出日時 2026-06-22 00:18:32
言語 C++23
(gcc 15.2.0 + boost 1.89.0)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 709 ms / 2,000 ms
コード長 8,753 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 1,549 ms
コンパイル使用メモリ 175,612 KB
実行使用メモリ 6,400 KB
最終ジャッジ日時 2026-06-22 00:18:43
合計ジャッジ時間 7,059 ms
ジャッジサーバーID
(参考情報)
judge3_0 / judge1_1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 15
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <iostream>
#include <atcoder/modint>
#include <vector>
#include <cassert>

using namespace std;
using namespace atcoder;
using mint = modint998244353;
template<typename T = mint>
struct matrix {
    int n,m;
    vector<vector<T>> A;
    matrix(vector<vector<T>> _A): A(_A){
        n = A.size();
        if(n){
            m = A[0].size();
            for(int i=1;i<n;i++) assert(A[i].size()==m);
        }
    }
    matrix(int _n,int _m): n(_n), m(_m){
        A.resize(n);
        for(int i=0;i<n;i++) A[i].resize(m);
    }
    vector<T>& operator[](int row){return A[row];}
    const vector<T>& operator[](int row) const { return A[row]; }
    matrix operator+(const matrix& B) const {
        assert(n == B.n && m == B.m);
        matrix<T> C(n, m);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                C[i][j] = A[i][j] + B[i][j];
            }
        }
        return C;
    }    
    matrix& operator+=(const matrix& B) {
        assert(n == B.n && m == B.m);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                A[i][j] += B[i][j];
            }
        }
        return *this;
    }
    matrix operator-(const matrix& B) const {
        assert(n == B.n && m == B.m);
        matrix<T> C(n, m);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                C[i][j] = A[i][j] - B[i][j];
            }
        }
        return C;
    }
    matrix& operator-=(const matrix& B) {
        assert(n == B.n && m == B.m);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                A[i][j] -= B[i][j];
            }
        }
        return *this;
    }
    matrix operator*(const matrix& B) const {
        assert(m == B.n); // 列数と行数が一致すること
        matrix<T> C(n, B.m);
        for (int i = 0;i<n;i++) {
            for (int j = 0;j<B.m;j++) {
                for (int k = 0;k<m;k++) {
                    C[i][j] += A[i][k] * B[k][j];
                }
            }
        }
        return C;
    }
    matrix& operator*=(const matrix& B) {
        assert(m == B.n);
        *this = *this * B;
        return *this;
    }
    // 単位行列
    matrix e(int n){
        matrix ret(n,n);
        for(int i=0;i<n;i++) ret[i][i] = 1;
        return ret;
    }
    // Tr
    matrix Tr(){
        matrix ret(m,n);
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++) ret[i][j] = A[j][i];
        }
        return ret;
    }
    // A^dを返す
    // 破壊的にすることで高速化の余地あり
    // verified: https://judge.yosupo.jp/submission/265168
    matrix pw(long long d){
        assert(n==m);
        matrix ret = e(n);
        matrix B = A;
        while(d){
            if(d&1) ret *= B;
            B *= B; d /= 2;
        }
        return ret;
    }
    // 掃き出したもの (破壊的にして高速化の余地あり)
    // 行列式でも使うので、isSwapで管理
    // O(min(n,m)*nm)
    // [掃き出し後の行列,[行列式を求めるときに-1をかけるか?,pivotになる列]] を返却
    pair<matrix,pair<bool,vector<int>>> gaussian(){
        matrix B = A;
        bool isSwap = false;
        // 今r行c列まで見た
        // O(min(r,c)*r*c)
        vector<int> pivots;
        for(int r = 0,c = 0;r<n && c<m;){
            int nx = -1;
            for(int i=r;i<n;i++){
                if(B[i][c].val()){
                    nx = i;
                    break;
                }
            }
            if(nx==-1){
                c++;
                continue;
            }
            // 以下B[nx][c]!=0
            // B[r][c]!=0にする
            swap(B[r],B[nx]);
            pivots.push_back(c);
            if(nx!=r) isSwap ^= true;
            for(int i2=0;i2<n;i2++){
                if(B[i2][c]==0) continue;
                if(i2==r){
                    T x = B[r][c];
                    for(int j=c;j<m;j++) B[i2][j] /= x;
                }else{
                    T x = B[i2][c]/B[r][c];
                    for(int j=c;j<m;j++) B[i2][j] -= x*B[r][j];
                }
            }
            r++; c++;
        }
        return {B,{isSwap,pivots}};
    }
    // verified: https://judge.yosupo.jp/submission/265176
    T det(){
        assert(n==m);
        auto [B,f] = gaussian();
        T ret = 1;
        for(int i=0;i<n;i++) ret *= B[i][i];
        if(f) ret *= -1;
        return ret;
    }
    // O(min(n,m)nm)でrank計算
    // 行列を複製するので高速化の余地あり
    // verified: https://judge.yosupo.jp/submission/265180
    // 多分転置しなくていい
    int rank(){
        matrix _A = n > m ? Tr() : A;
        auto [B,_] = _A.gaussian();
        // Traceを取っている可能性があることに注意
        int ret = min(n,m);
        for(int i=0;i<min(n,m);i++){
            for(int j=0;j<max(n,m);j++){
                if(B[i][j].val()) break;
                if(j==max(n,m) - 1) ret--;
            }
        }
        return ret;
    }

    //O(n^3)で逆行列を返す
    //ないときは0*0行列を返す
    // verify: https://judge.yosupo.jp/submission/265184
    matrix inverse(){
        assert(n==m);
        matrix B(n,2*n);
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++) B[i][j] = A[i][j];
            B[i][n + i] = 1;
        }
        auto [C,_] = B.gaussian();
        for(int i=0;i<n;i++){
            if(C[i][i]==0) return matrix(0,0);
            for(int j=0;j<i;j++){
                mint x = C[j][i]/C[i][i];
                for(int k=i;k<2*n;k++){
                    C[j][k] -= C[i][k]*x;
                }
            }
            mint x = C[i][i];
            for(int j=i;j<2*n;j++){
                C[i][j] /= x;
            }
        }
        matrix ret(n,n);
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++) ret[i][j] = C[i][j + n];
        }
        return ret;
    }
    // O((n + m)*n*m)でkerを求める
    // 掃きだしはO(min(n,m)*n*m)でできるが、kerはm次元vectorがmax(n,m) - rank(A)なので、
    // kerの基底の行列のサイズがO((n + m)*m)くらいにはなりうる
    // verifyは出来ていない (これ https://atcoder.jp/contests/utpc2024/tasks/utpc2024_n でしたかったが、計算量的にTLEする)
    matrix kernel() {
        assert(n != 0); // 空の行列に対してカーネルは無意味
        auto [B,_] = gaussian();
        auto [__,pivots] = _;
        // 基底ベクトルを求める
        vector<vector<mint>> basis;
        vector<bool> is_pivot(m, false);
        for (int p : pivots) is_pivot[p] = true;
        for (int c = 0; c < m; ++c) {
            if (!is_pivot[c]) {
                // pivotではない列について1をたてて、掃き出し後の行列で0でない行については、
                // pivot (掃き出し後で1になっている) で調整するイメージ
                vector<mint> vec(m, 0);
                vec[c] = 1;
                for (int i = 0; i < pivots.size(); ++i) {
                    vec[pivots[i]] = -B[i][c];
                }
                basis.push_back(vec);
            }
        }
        return matrix(basis);
    }   
};

#include <iostream>
#include <string>
#include <vector>
#include <atcoder/modint>
#include <array>

using namespace std;
using namespace atcoder;
using mint = modint998244353;
typedef long long ll;
int main(){
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);
    string s; cin >> s;
    ll k; cin >> k;
    int i,j,l,n = s.size();
    // 先頭10個 := 末尾がiの総和
    // 後半10個 := 末尾がiの個数
    // 20変数の線形和をもって計算する
    const int SZ = 20;
    array<array<mint,20>,20> v;
    for(i=0;i<SZ;i++) v[i][i] = 1;
    array<mint,SZ> ar_v,ar_c; 
    for(i=0;i<n;i++){
        for(j=0;j<SZ;j++) ar_v[j] = 0, ar_c[j] = 0;
        for(j=0;j<10;j++){
            for(l=0;l<SZ;l++){
                ar_v[l] += v[j][l]*10 + v[10 + j][l]*(s[i] - '0');
                ar_c[l] += v[10 + j][l];
            }
        }
        for(j=0;j<SZ;j++){
            v[s[i] - '0'][j] = ar_v[j];
            v[s[i] - '0' + 10][j] = ar_c[j];
        }
    }
    matrix<mint> A(SZ,SZ);
    for(i=0;i<SZ;i++){
        for(j=0;j<SZ;j++) A[i][j] = v[i][j];
    }
    matrix<mint> B = A.pw(k);
    matrix<mint> C(SZ,1);
    // 先頭0の文字だけ持ってるという解釈
    C[10][0] = 1;
    matrix<mint> D = B*C;
    mint ans = 0;
    for(i=0;i<10;i++) ans += D[i][0];
    cout << ans.val() << "\n";
    // for(i=10;i<SZ;i++){
    //     for(j=10;j<SZ;j++){
    //         cout << A[i][j].val() << " ";
    //     }
    //     cout << "\n";
    // }
}
0