[考试反思]0110省选模拟5:信仰
又倒一啦
10分真是个熟悉的成绩(虽然没有爆零熟悉)
状态一直低迷,没什么好说的
答题策略的话。。。就那样吧
一眼看出T1是结论题,没有部分分,不好写。
但T2是伯努利数的差不多是模板题了,于是就奔着它去了。
没有注意$0^0=1$。过不去样例最后交的暴力
没有抽时间认真打T3的暴力,因为至多30分。
因为现在30分和0分对我来说是一样的。我是要靠着省选翻盘,拿大众分的话肯定还是退役,求个稳,4个月白学。
现在的答题策略的确不能像联赛时一样了。现在求稳是赢不了了,在稳住200和争取230里我可能会选择稳住200。
但是在稳住30和争取100里,选择也是显然的。
现在只能拼啊,联赛的劣势只能接受了,只要拿不到高分,大众分和爆零是等价的。
我不习惯这么大的风险,但是谁叫这是我自己造就的呢?
其实也是自己的大锅。伯努利数的式子又忘了,花两个小时写个高消找规律考场上又「发明」伯努利数了。
T1:青蛙
题意:m蛤k石头,蛤要从1到n,每个石头要被踩恰好一次。第$i$只蛤单次跳跃距离大于$k$时有$w_i$代价。最小化代价。
$m,k\le 10^5$
结论题:最贵的几只蛤尽量免费跳过去剩下的一步跳。若所有蛤都不能免费那么最便宜的一个一个跳石头其余一步跳。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int c[100005],a[100005],n,m,k,d; 4 bool chk(int x){ 5 if(k<x||a[x]-1>d||n-a[k-x+1]>d)return false; 6 for(int i=x+1;i<=k;++i)if(a[i]-a[i-x]>d)return false; 7 return true; 8 } 9 int main(){ 10 int t;cin>>t;while(t-->0){ 11 cin>>n>>m>>k>>d; 12 for(int i=1;i<=m;++i)scanf("%d",&c[i]); 13 for(int i=1;i<=k;++i)scanf("%d",&a[i]); 14 sort(a+1,a+1+k);sort(c+1,c+1+m); 15 if(n-1<=d){puts("0");continue;} 16 int l=1,r=m,ans=0;long long w=0; 17 while(l<=r)if(chk(l+r>>1))l=ans=l+r>>1,l++;else r=(l+r>>1)-1; 18 if(!ans){ 19 for(int i=2;i<=m;++i)w+=c[i]; 20 if(a[1]-1>d)w+=c[1]; 21 for(int i=2;i<=k;++i)if(a[i]-a[i-1]>d)w+=c[1]; 22 if(n-a[k]>d)w+=c[1]; 23 }else for(int i=1;i<=m-ans;++i)w+=c[i]; 24 printf("%lld\n",w); 25 } 26 }
T2:一起自习的日子
题意:给定$k,a,n,d$求$\sum\limits_{i=0}^{n} \sum\limits_{j=1}^{a+id} \sum\limits_{l=1}^{j} l^k$
$k \le 3000,mod=1234567891$
注意加法爆$int$
个人认为是板子题。三次自然数幂和展开+一次二项式展开就可以一层一层的预处理做了。
%%%Duanyue,答案是多项式,多次插值即可。
自然数幂和注意$0^0=1$。体现在代码中就是$cal[0]=n+1$
1 //mod+mod>max_int 2 #include<bits/stdc++.h> 3 using namespace std; 4 #define int long long 5 #define mod 1234567891 6 int C[3333][3333],B[3333],iv[3333],k,a,n,d,cal[3333],pwn[3333],cal2[3333],pwd[3333],pwa[3333],cal3[3333],ans; 7 int qp(int b,int t,int a=1){for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;} 8 signed main(){//freopen("1.in","r",stdin); 9 for(int i=0;i<=3009;++i)for(int j=C[i][0]=1;j<=i;++j)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; 10 for(int i=0;i<=3300;++i)iv[i]=qp(i,mod-2); 11 for(int i=0;B[i]=i+1,i<=3009;++i){for(int j=0;j<i;++j)B[i]=(mod+B[i]-C[i+1][j]*B[j]%mod)%mod;B[i]=B[i]*iv[i+1]%mod;} 12 // for(int i=0;i<=15;++i)cout<<B[i]<<' ';cout<<endl; 13 int t;cin>>t;while(t-->0){ 14 cin>>k>>a>>n>>d; 15 pwn[0]=pwd[0]=pwa[0]=1;ans=0; 16 for(int i=1;i<=k+8;++i)pwn[i]=pwn[i-1]*n%mod,pwa[i]=pwa[i-1]*a%mod,pwd[i]=pwd[i-1]*d%mod; 17 for(int i=0;i<=k+8;++i)for(int j=0;j<=i;++j)cal[i]=(cal[i]+C[i+1][j]*B[j]%mod*pwn[i+1-j])%mod; 18 for(int i=0;i<=k+8;++i)cal[i]=cal[i]*iv[i+1]%mod;cal[0]=n+1; 19 for(int i=0;i<=k+8;++i)for(int j=0;j<=i;++j)cal2[i]=(cal2[i]+pwd[j]*pwa[i-j]%mod*C[i][j]%mod*cal[j])%mod; 20 for(int i=0;i<=k+8;++i)for(int j=0;j<=i;++j)cal3[i]=(cal3[i]+C[i+1][j]*B[j]%mod*cal2[i+1-j])%mod; 21 for(int i=0;i<=k;++i)ans=(ans+C[k+1][i]*B[i]%mod*iv[k+2-i]%mod*cal3[k+1-i])%mod; 22 ans=ans*iv[k+1]%mod;cout<<ans<<endl;ans=0; 23 // for(int i=0;i<=n;++i)for(int j=1;j<=i*d+a;++j)for(int l=1;l<=j;++l)ans=(ans+qp(l,k))%mod; 24 // cout<<"ac:"<<ans<<endl;ans=0; 25 memset(cal,0,sizeof cal);memset(cal2,0,sizeof cal2);memset(cal3,0,sizeof cal3); 26 /* for(int p=0;p<=k;++p){ 27 int t1=0; 28 for(int x=0;x<=k-p+1;++x){ 29 int t2=0; 30 for(int t=0;t<=k-p+2-x;++t){ 31 int t3=0; 32 for(int y=0;y<=t;++y)t3=(t3+(C[t+1][y]*B[y]%mod*qp(n,t+1-y)))%mod; 33 t2=(t2+t3*qp(t+1,mod-2)%mod*C[k-p+2-x][t]%mod*qp(d,t)%mod*qp(a,k-p+2-x-t)%mod)%mod;//cout<<t<<' '<<t3*qp(t+1,mod-2)%mod<<endl; 34 } 35 t1=(t1+C[k-p+2][x]*B[x]%mod*t2)%mod; 36 } 37 ans=(ans+t1*C[k+1][p]%mod*B[p]%mod*qp(k-p+2,mod-2))%mod; 38 }cout<<ans*qp(k+1,mod-2)%mod<<endl;*/ 39 } 40 }
T3:字符串
题意:在线支持在末尾插入字符,询问区间出现次数大于等于2的字符串最大长度。$n,m\le 50000$
先令$ans[r][l]=ans[r-1][l]$。考虑插入字符对答案的贡献。设$lst$表示每个节点的串的最后一次出现位置。
克隆节点显然不影响。新建节点时,$np$的所有祖先会贡献答案,若$1 \le i \le lst[p]-len[p]$其$ans[r][i]$与$len[p]$取$max$
若$lst[p]-len[p]+1 \le i \le lst[p]$其$ans[r][i]$对$len[p]-i+lst[p]$取$max$
区间对常数取$max$,以及对等差数列取$max$。主席树+标记永久化可以卡过。而在$SAM$上爆跳父亲。
在更新完$ans$后这些祖先节点的$lst$更新为当前串长度。
最劣复杂度$O(n^2logn)$。但是空间先爆炸。
加一些优化,更新一条链时如果已经是当前这棵树的节点则不再新建,再加一点小剪枝在垃圾原数据下堪比正解。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 355555 4 int lz1[S<<8],lz2[S<<8],lc[S<<8],rc[S<<8],pc,rt[S]; 5 #define md (cl+cr>>1) 6 void modify1(int&p,int cpy,int lim,int l,int r,int w,int cl=1,int cr=100000){ 7 if(r<l)return;if(w<=lz1[cpy]||lz2[cpy]-cr+cl>=w){p=cpy;return;} 8 if(cpy<=lim)p=++pc,lc[p]=lc[cpy],rc[p]=rc[cpy],lz1[p]=lz1[cpy],lz2[p]=lz2[cpy]; 9 if(l<=cl&&cr<=r){lz1[p]=w;return;} 10 if(l<=cl+cr>>1)modify1(lc[p],lc[cpy],lim,l,r,w,cl,md); 11 if(r>cl+cr>>1)modify1(rc[p],rc[cpy],lim,l,r,w,md+1,cr); 12 } 13 void modify2(int&p,int cpy,int lim,int l,int r,int w,int cl=1,int cr=100000){ 14 if(r<l)return;if(w-cl+l<=lz2[cpy]||w-cl+l<=lz1[cpy]){p=cpy;return;} 15 if(cpy<=lim)p=++pc,lc[p]=lc[cpy],rc[p]=rc[cpy],lz1[p]=lz1[cpy],lz2[p]=lz2[cpy]; 16 if(l<=cl&&cr<=r){lz2[p]=w-cl+l;return;} 17 if(l<=cl+cr>>1)modify2(lc[p],lc[cpy],lim,l,r,w,cl,md); 18 if(r>cl+cr>>1)modify2(rc[p],rc[cpy],lim,l,r,w,md+1,cr); 19 } 20 int ask(int p,int pos,int cl=1,int cr=100000){ 21 if(cl==cr)return max(lz1[p],lz2[p]); 22 return max(max(lz1[p],lz2[p]-pos+cl),pos<=md?ask(lc[p],pos,cl,md):ask(rc[p],pos,md+1,cr)); 23 } 24 int f[S],c[26][S],len[S],Len,las=1,SAMpc=1,lst[S];char chs[S]; 25 void insert(int C){ 26 int p=las,np=las=++SAMpc,q,nq;len[np]=len[p]+1;++Len;rt[Len]=rt[Len-1];lst[np]=Len; 27 for(;p&&!c[C][p];p=f[p])c[C][p]=np; 28 if(!p){f[np]=1;goto U;} 29 if(len[q=c[C][p]]==len[p]+1){f[np]=q;goto U;} 30 nq=++SAMpc; len[nq]=len[p]+1; for(int i=0;i<26;++i)c[i][nq]=c[i][q]; 31 f[nq]=f[q]; f[q]=f[np]=nq; lst[nq]=lst[q]; 32 for(;c[C][p]==q;p=f[p])c[C][p]=nq; 33 U:p=f[np];int X=pc;while(p){ 34 modify1(rt[Len],rt[Len],X,1,lst[p]-len[p],len[p]); 35 modify2(rt[Len],rt[Len],X,lst[p]-len[p]+1,lst[p],len[p]); 36 lst[p]=Len;p=f[p]; 37 } 38 } 39 int main(){//freopen("string10.in","r",stdin); 40 int m,la=0;scanf("%s%d",chs+1,&m); 41 for(int i=1;chs[i];++i)insert(chs[i]-'a'); 42 while(m-->0){ 43 int opt;scanf("%d",&opt); 44 if(opt==1){ 45 scanf("%s",chs);int x=chs[0]-'a'; 46 x=(x+la)%26;insert(x); 47 }else{ 48 int l,r,ans=0;scanf("%d%d",&l,&r); 49 l=(l+la-1)%Len+1,r=(r+la-1)%Len+1; 50 if(l>r)swap(l,r); 51 printf("%d\n",la=ask(rt[r],l)); 52 } 53 } 54 }
然而
考虑如何减少修改次数。可以发现每次修改都是到根的,类似$lct::access$。这样每次$access$之后每个$splay$中$lst$相同。
对于同一个$splay$,我们发现$lst$相同的点对答案的真正贡献只有$len$最大的那一个,所以$splay$里维护最大的$len$每次跳实链更新。
要注意原来很简单的克隆点的操作,需要判断父子的$lst$是否相同以特殊处理。
这样只有在虚边时会更新答案,期望$log$次,每次$O(log)$。总复杂度$O(nlog^2n)$
加个二分答案就变成区间修改单点查询了?
太菜,代码肯定写不出来,弃了。