Day 2:
K-hash:
这道题其实只要懂了后缀自动机就不难了。不过我还是做了很久。本来以为用个sort就可以让所有的节点按val从小到大排序,然后就能递推了。调试良久后发现排序后数组中每个元素的地址不会发生变化,只是值发生变化了,所以排序是没用的(因为p->o[i]和p->f都是指向的不变化的地址)。然后用priority_queue强行扭过了,速度还挺快,得瑟一下O(∩_∩)O~~
(不知道zimpha怎么写的,跟开挂一样,又快内存又小,有机会的话找他请教一下。)
这题其实直接按val值bfs一下就能得到按val值从小到大排序的点了...注意bfs的时候每次val值只能加1.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 #include <queue> 7 #define FOR(i,l,r) for(int i=(l);i<=(r);i++) 8 #define FORD(i,r,l) for(int i=(r);i>=(l);i--) 9 #define rep(i,n) for(int i=0;i<(n);i++) 10 #define res(i,s) for(int i=0;((s)[i]);i++) 11 using namespace std; 12 typedef long long LL; 13 const int maxn=50001; 14 15 char s[maxn]; 16 int k; 17 18 struct sam{ 19 sam *o[10],*f; 20 int val; 21 LL v[32]; 22 }ssam[maxn*2],*bt,*last; int sct; 23 sam *newd(int val) 24 { 25 sam *p=&ssam[sct++]; 26 rep(i,10) p->o[i]=0; 27 p->f=0; p->val=val; 28 rep(i,k) p->v[i]=0; 29 return p; 30 } 31 void trans(char s) 32 { 33 int d=s-'0'; 34 sam *p=last,*np=newd(p->val+1); last=np; 35 for (; p&&!p->o[d]; p=p->f) p->o[d]=np; 36 if (!p) np->f=bt; 37 else if (p->o[d]->val==p->val+1) np->f=p->o[d]; 38 else 39 { 40 sam *q=p->o[d],*nq=newd(p->val+1); 41 memcpy(nq->o,q->o,sizeof(q->o)); 42 nq->f=q->f; q->f=np->f=nq; 43 for (; p&&p->o[d]==q; p=p->f) p->o[d]=nq; 44 } 45 } 46 void cl(sam *p) 47 { 48 for (int i=(p==bt?1:0); i<10; i++) 49 if (p->o[i]) 50 { 51 for (int j=0; j<k; j++) 52 p->o[i]->v[(j*10+i)%k]+=p->v[j]; 53 } 54 } 55 56 LL ans[32]; 57 void init() { sct=0; last=bt=newd(0); } 58 void bl(sam *t,string s) 59 { 60 cout<<s<<endl; 61 rep(i,10) 62 if (t->o[i]) 63 bl(t->o[i],s+char('0'+i)); 64 } 65 int main(){ 66 while (scanf("%s%d",s,&k)!=EOF) 67 { 68 init(); 69 res(i,s) trans(s[i]); 70 //bl(bt,""); 71 priority_queue<pair<int,int>, deque<pair<int,int> >, greater<pair<int,int> > > q; 72 rep(z,sct) q.push(make_pair(ssam[z].val,z)); 73 bt->v[0]=1; 74 while (!q.empty()) cl(&ssam[q.top().second]),q.pop(); 75 rep(i,k) ans[i]=0; 76 FOR (i,1,sct-1) rep(j,k) ans[j]+=ssam[i].v[j]; 77 if (bt->o[0]) ans[0]++; 78 rep(i,k) printf("%s%lld", i==0?"":" ", ans[i]); 79 puts(""); 80 } 81 return 0; 82 } 83 84 //greater是最小堆,priority_queue默认是最大堆
Day 4:
Fixed Point
题目总结:
这种数论动规的关键点是在“与上届相等的数的处理”上,只要这个弄懂了,这种题应该就都会做了。
因为和上届相等的数最多只有一个,所以我用一个equal来记录是否有满足条件的上届。而其他小于上届的数用f数组储存。
策略只有取1和取0。
小于上届的数可以随便取。
equal的状态转移要好好想想:
当前位为1:取1则equal保留,取0则equal可以转移到f数组。
当前位为0:取0则equal保留。取1就比上届大了,舍去。
一般性总结:
1.打草稿分析样例很有效果。
2.直接用不加证明的结论这种习惯不好。这道题中以及Coprime Sequence中已经深有体会。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #define FOR(i,l,r) for(int i=(l);i<=(r);i++) 6 #define FORD(i,r,l) for(int i=(r);i>=(l);i--) 7 #define rep(i,n) for(int i=0;i<(n);i++) 8 using namespace std; 9 typedef long long LL; 10 typedef unsigned int ui; 11 ui get2[33]; 12 13 LL f[33][33]; 14 LL gao(ui R, ui c, int k, int flag) 15 { 16 memset(f,0,sizeof(f)); 17 LL equal=1; int gs0=0; 18 FORD (z,31,0) 19 { 20 //0 21 if (flag>=0 || !(c&get2[z])) 22 { 23 FOR (i,1,32-z) f[z][i]=f[z+1][i-1]; 24 if (R&get2[z]) f[z][gs0+1]+=equal; 25 } 26 //1 27 if (flag<=0 || (c&get2[z])) 28 { 29 FOR (i,0,31-z) f[z][i]+=f[z+1][i]; 30 } 31 if ((R&get2[z]) && !(flag<=0 || (c&get2[z]))) equal=0; 32 if (!(R&get2[z]) && !(flag>=0 || !(c&get2[z]))) equal=0; 33 gs0+=!(R&get2[z]); 34 } 35 LL ret=0; 36 FOR (i,0,32) if (abs(32-i-i)<=k) ret+=f[0][i]; 37 if (abs(32-gs0*2)<=k) ret+=equal;//~~ 38 return ret; 39 } 40 41 ui L,R,c; int k; 42 LL gao0(int flag) 43 { 44 LL ret=gao(R,c,k,flag); 45 if (L) ret-=gao(L-1u,c,k,flag); 46 return ret; 47 } 48 49 int main() 50 { 51 get2[0]=1; for (int i=1;i<=32;i++) get2[i]=get2[i-1]<<1; 52 53 int T; scanf("%d",&T); 54 LL ans1,ans2,ans3; 55 FOR (z,1,T) 56 { 57 scanf("%u%u%u%d",&L,&R,&c,&k); 58 ans1=gao0(1); 59 if (c!=0) ans2=0; else ans2=gao0(0); 60 ans3=gao0(-1); 61 printf("Case %d: %lld %lld %lld ",z,ans1,ans2,ans3); 62 } 63 return 0; 64 }