后缀自动机......慢慢体会ing。
这个题的题意是给你一堆数字串,让你去重以后问所有字串代表的数字的和(mod 2012)
这个题n是10^6级别的,所以要充分利用重复字串的性质。这么考虑,当有许多子串的都添一个末位数字K时,这些子串的和就是原来字串的和*10+子串个数*k。其实这些字串在自动机里面对应的就是一个状态,当这些状态都存在一个指向Q的转移时,就可以把这些状态的sum累积到Q上。这充分利用SAM自动机中转移函数的特点(tran[p][w]表示在p状态后面添加一个字符w,将成为哪一个状态的后缀)。这样想的话,每一个状态上累加的sum实际上是这个状态所有后缀的sum和,而这些后缀的共同特点就是最后一位数字相同!所以我们可以用同样的方式转移这些后缀的sum。
不知道我说清楚了没,感觉后缀自动机这东西还得慢慢体会,不能着急......
str2int
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 120000 7 #define inf 100000000 8 using namespace std; 9 struct node 10 { 11 node *ch[12],*pre; 12 int len,cnt,sum; 13 }sam[maxn],*rk[maxn],*rot,*now; 14 char s[maxn]; 15 int wv[maxn]; 16 int n,m,num,ans; 17 18 void insert(int w) 19 { 20 node *p=now,*np=&sam[++num]; 21 np->len=p->len+1; 22 while (p&&p->ch[w]==0) p->ch[w]=np,p=p->pre; 23 if (!p) np->pre=rot; 24 else 25 { 26 node *q=p->ch[w]; 27 if (p->len+1==q->len) np->pre=q; 28 else 29 { 30 node *nq=&sam[++num]; 31 *nq=*q; 32 nq->len=p->len+1; 33 np->pre=q->pre=nq; 34 while (p&&p->ch[w]==q) p->ch[w]=nq,p=p->pre; 35 } 36 } 37 now=np; 38 } 39 40 int main() 41 { 42 while (scanf("%d",&n)!=EOF) 43 { 44 rot=now=&sam[num=1]; 45 for (int i=1;i<=n;i++) 46 { 47 scanf("%s",s); 48 m=strlen(s); 49 for (int i=0;i<m;i++) insert(s[i]-'0'); 50 insert(10); 51 } 52 memset(wv,0,sizeof(wv)); 53 for (int i=1;i<=num;i++) wv[sam[i].len]++; 54 for (int i=1;i<=num;i++) wv[i]+=wv[i-1]; 55 for (int i=num;i>=1;i--) rk[wv[sam[i].len]--]=&sam[i]; 56 //for (int i=1;i<=num;i++) cout<<rk[i]->len<<' '; 57 ans=0; 58 rk[1]->cnt=1; 59 rk[1]->sum=0; 60 for (int i=1;i<=num;i++) 61 { 62 node *p=rk[i]; 63 for (int w=0;w<10;w++) 64 if (p->ch[w]) 65 { 66 if (i==1&&w==0) continue; 67 node *q=p->ch[w]; 68 q->cnt+=p->cnt; 69 q->cnt%=2012; 70 q->sum+=(p->sum*10+p->cnt*w); 71 q->sum%=2012; 72 } 73 ans+=p->sum; 74 ans%=2012; 75 } 76 printf("%d\n",ans); 77 memset(sam,0,sizeof(sam)); 78 } 79 return 0; 80 }