對於一連串的替換操作,可以發現到其實「1」這個字不管是落在原字串的哪裡,最後一定都會變成一個固定的很長的字串,這對於0,...,9都成立。因此如果我們能算出每個字元在經過所有的替換之後變成的字串的長度,和這個字串本身模10^9+7的值,就可以O(n)算出答案了。具體來說,假設原字串是123456,並且一連串的操作把6變成了777,5變成了888,4變成了999,那麼我們可以算出原本的4變成999之後應該要乘上10的幾次才會是他實際上的值,不難發現他其實就是「5最後的字串長度+6最後的字串長度」,所以就得到了999\cdot 10^6。只要這樣算出每個字元的答案的總和就可以了。
至於如何求最後字串長度和模10^9+7的值(以下稱其為len值和val值),可以把整個過程倒過來看。當沒有進行任何替換時,0~9的長度都是1,val值則是他本身。再來觀察只有進行最後一次替換時0~9的len值和val值應該會是多少,假設這次替換是x->S,那麼可以發現此時len[x]=S的長度,val[x]則變成了S代表的值,其他數的len,val則不變。
再來我們的目的是,假設現在已經有了「考慮第i個替換到最後一個替換時,每個數的len和val值」,想要推到「考慮第i-1個到最後一個時」的兩種值,而這很容易從上面觀察出一般的情形,可以發現其實新的len[x]值就是由舊的len陣列中,把S的每一個字元代入再加起來的結果,而val[x]則比較複雜,不過基本上和前面所講的「O(n)算出原數列的答案」的精神類似,因為我們知道S中的每個字元在經過了第i次到最後一次替換之後會變成什麼值了,所以就可以求出S經過了第i次到最後一次替換後會變成多少,而這個值就是新的val[x]了。
最後要注意到,len值會爆 long long,事實上我們可以把len值拿去模10^9+7-1,因為其實可以發現len值就是被用來放在10的冪次,以求得一個字元真正代表的值的,而因為10^9+7是一個質數(記為p),由費馬小定理知a^{p-1}\equiv 1(mod p),因此把len值加減p-1並不會影響結果,所以模p-1才會是對的。
code :
#include<bits/stdc++.h> #define LL long long #define MOD 1000000007 using namespace std; const int maxn=100000+10 ; LL pow(LL x,LL n,int mod) { if(n<=1) return n ? x : 1LL ; LL t=pow(x,n/2,mod) ; if(n&1) return (t*t%mod)*x%mod ; else return t*t%mod ; } LL pw[maxn] ; char s[maxn],t[maxn] ; int n,d[maxn] ; string st[maxn] ; LL len[maxn],val[maxn] ; main() { pw[0]=1 ; for(int i=1;i<maxn;i++) pw[i]=pw[i-1]*10%MOD ; scanf("%s%d",s,&n) ; for(int i=1;i<=n;i++) { scanf("%s",t) ; d[i]=t[0]-'0' ; st[i]=string(t+3,t+strlen(t)) ; } for(int i=0;i<10;i++) len[i]=1 , val[i]=i ; for(int i=n;i>=1;i--) { int sz=st[i].size() ; LL l2=0LL , val2=0LL ; for(int j=sz-1;j>=0;j--) { val2=(val2+val[st[i][j]-'0']*pow(10,l2,MOD))%MOD ; l2=(l2+len[st[i][j]-'0'])%(MOD-1) ; } len[d[i]]=l2 , val[d[i]]=val2 ; } LL ans=0LL ; for(int i=strlen(s)-1,nowl=0;i>=0;i--) { ans+=val[s[i]-'0']*pow(10,nowl,MOD) ; ans%=MOD ; nowl=(nowl+len[s[i]-'0'])%(MOD-1) ; } printf("%I64d\n",ans) ; }
沒有留言:
張貼留言