アナログNOIP2019の6361. [2019年9月18日]シマガツオ番号

タイトル

効果の対象に

あなたの間隔与える\を([L、R&LT] \) ハイからローへと逆の数の範囲内のそれぞれの10進整数オンデマンド。


歴史上のものがたり

これはビットDPの数で始まり......
結果は、崩壊の精神から転送されていない......


正解

私のSBの実践についての最初の話。
第一セットの\(のf_i \)の押下を表し\(iは\) からローからハイに、ビット\(0 \)開始に貢献します)。
したがって、このシフトは、次のとおりです。

  1. 計算\(私は\)が拠出するビットです。これは、計算する少し難しいかもしれ貢献であるので、私は、前処理\(H_ {I、J、 0/1} \)を押しているかどうかを示す\(iは\)ビット、\(0 \)のために\(I \)ビット(jは\)\寄与(\(J \)がされている(iは\)\後のビットを\([0,9] \)の間の数)。だから、この事がある\(H_は{I、a_iを} \)
  2. \(I-1 \)から転送されるビット、\(I-1 \)ビットが押圧されます。明らかに\(I-F_ {}。1 \)
  3. \(I-1 \)から転送されるビット、\(I-1 \)ビットが押圧されていません。これは列挙選択である\(X \)、寄与は、\(\ sum_ {X = 0 } ^ {A_ {I-1} -1} | X <J | * 10 ^ {I-1} + G_ { +} 2-I(I-1)* 10 ^ {2} I-X \)\(G_i \)を表す\(0 \)にビットを(私は\)\任意の選択された番組のビット数。この式は、ここに出て遊んでいない、簡素化することができます。

考えてみましょう(グラム\)\転送、転送が明確に正面から来ているかと、この1つの貢献を追加します。
すなわち\(g_i = 10 * G_ { I-1} + \ FRAC {(0 + 9)×10}は{2} iが10 ^ I \ *)

次に考える\(時間\)転送。
\(H_ {I、J、J = 0} * 10 + 10 * ^ H_ {I-I 1、J、0} \)
\(H_ {I、J ,. 1} \)比較的複雑な転写。そして\(F \)転送がやや似ています。
\(NUM \)される(0 \)\する(I-1 \)\ビットの10進形式、アナログする即ち全体デジタル(I 10 ^ \)\を
\(H_ {I、J、 1} = | a_iを<J | *(NUM + 1)+ H_ {I-1、J、1} +(\ sum_ {X = 0} ^ {A_ {I-1} -1} | X <J | *
10 ^ {I-1} + H_ {I-2、jは、0})\) もそれを簡略化することができます。

簡素化した後、この転送での時間複雑見つけるのは簡単です\(O(10N)\)です。

また、より高度な実践が簡単があるかもしれません:
ハイからローへの列挙\を(私は\)を表し、\は(私は\)拠出の時間をメモしキャップを押すと、ビット。
だから、3人の貢献があります。

  1. より高い\(私は\)ビットと\(私は\)貢献。そのための\(私は\)の上限を押しているので、すべての前に現れることができる数字の数はバケツで記録することができます。
  2. \(iは\)位置と下\(I \)寄与ビット。私たちは、その数字のプログラムが計算を背中合わせにキャップを押すことになるので、上限は、押しされていないの後ろに強制します。列挙1つのバック、バック、あなたがいずれかを選ぶことができます。
  3. より高い\(iは\)位置と下\(iは\)寄与ビット。この計算は、同様に追いつきます。

そして、なくなって。


コード

この問題は、あまりにも嫌です......非常にチートスの詳細を......

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define mo 998244353
#define LEN 500010
#define ll long long
char str[LEN];
int n,m,L[LEN],R[LEN];
inline void read(int a[],int &n){
    scanf("%s",str);
    n=strlen(str);
    for (int i=0;i<n;++i)
        a[i]=str[n-i-1]-'0';
    a[n]=0;
}
ll _pow10[LEN],*pow10=_pow10+1;
ll f[LEN],g[LEN],h[LEN][10][2];
inline ll get(int a[],int n){
    if (n<=0)
        return 0;
    g[0]=0;
    for (int i=1;i<=n;++i)
        g[i]=(g[i-1]*10+45*i*pow10[i-1])%mo;
    for (int j=0;j<=9;++j)
        h[0][j][0]=j,h[0][j][1]=(a[0]<j);
    for (int i=1,num=a[0];i<=n;num=(num+pow10[i]*a[i])%mo,++i)
        for (int j=0;j<=9;++j){
            h[i][j][0]=(h[i-1][j][0]*10+j*pow10[i])%mo;
            int tmp=min(a[i-1],j);
            h[i][j][1]=((a[i]<j?num+1:0)+h[i-1][j][1]+(i-2>=0?h[i-2][j][0]*a[i-1]:0)+tmp*pow10[i-1])%mo;
        }
    f[0]=0,f[1]=min(a[0]+1,a[1]);
    for (int i=2;i<=n;++i)
        f[i]=(h[i][a[i]][1]+f[i-1]+a[i-1]*g[i-2]+(i-1)*pow10[i-2]*((a[i-1]-1)*a[i-1]>>1))%mo;
    return f[n];
}
int main(){
    freopen("pair.in","r",stdin);
    freopen("pair.out","w",stdout);
    pow10[0]=1;
    for (int i=1;i<=500001;++i)
        pow10[i]=pow10[i-1]*10%mo;
    int T,ty;
    scanf("%d%d",&T,&ty);
    while (T--){
        read(L,n),read(R,m);
        L[0]--;
        for (int i=0;L[i]<0;++i)
            L[i+1]--,L[i]+=10;
        if (!L[n-1])
            n--;
        printf("%lld\n",(get(R,m)-get(L,n)+mo)%mo);
    }
    return 0;
}

概要

実際には、この問題は難しい考え方ではありません......
しかし、このプロセスは、あまりにも疲れている......

おすすめ

転載: www.cnblogs.com/jz-597/p/11579009.html