2017-3-16校内训练
一个学长出题,还算良心吧,有一题没打,280/400。
T1.BBS造墙
题目大意:一个长度为n的序列,你可以做任意次一个操作:把当前序列的第一个数放到最后一个去,要求输出能达成的字典序最大的序列。(n<=2000000)
我的做法:不会做,把序列复制一份接到后面直接后缀数组,DC3又难打又会爆内存(128M),就打了倍增,拿了80分,复杂度O(nlogn),顺便还写了个大约是O(n)的计数排序233。(后来想到不复制序列,要用后面的Rank时直接去前面找好像就不会爆内存,而且常数只有一半)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 4000000 int a[MN+5],s[MN+1],rk[2][MN+5],sa[2][MN+5]; int main() { freopen("minecraft.in","r",stdin); freopen("minecraft.out","w",stdout); int n=read(),l,i,p,q; for(i=1;i<=n;++i)a[i]=a[i+n]=read();n<<=1; for(i=1;i<=n;++i)++s[a[i]&65535]; for(i=65536;i--;)s[i]+=s[i+1]; for(i=n;i;--i)sa[0][s[a[i]&65535]--]=i; memset(s,0,sizeof(int)*(1<<16)); for(i=1;i<=n;++i)++s[a[i]>>16]; for(i=1<<16;i--;)s[i]+=s[i+1]; for(i=n;i;--i)sa[1][s[a[sa[0][i]]>>16]--]=sa[0][i]; for(i=1;i<=n;++i)rk[1][sa[1][i]]=rk[1][sa[1][i-1]]+(a[sa[1][i-1]]!=a[sa[1][i]]); for(p=0,q=l=1;l<n;l<<=1,p^=1,q^=1) { for(i=1;i<=n;++i)s[rk[q][sa[q][i]]]=i; for(i=0;i<l;++i)sa[p][s[rk[q][n-i]]--]=n-i; for(i=n;i;--i)if(sa[q][i]>l)sa[p][s[rk[q][sa[q][i]-l]]--]=sa[q][i]-l; for(i=1;i<=n;++i)rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+ (rk[q][sa[p][i-1]]!=rk[q][sa[p][i]]||rk[q][sa[p][i-1]+l]!=rk[q][sa[p][i]+l]); } for(i=1;sa[q][i]>n>>1;++i); for(l=0;l<n>>1;++l)printf("%d ",a[sa[q][i]+l]); fclose(stdin);fclose(stdout);return 0; }
正解:先复制一份序列,我们假设当前答案的第一个位置为i,设个j从i+1开始作为新答案的第一个位置,开始比较a[i],a[j],a[i+1],a[j+1]……直到比较到a[i+k]不等于a[j+k],如果a[i+k]<a[j+k],我们令i=j,重新开始比较,否则我们令j=j+k+1,重新开始比较。分各类情况讨论我们发现这样做是不会漏掉可能成为答案的位置的并且复杂度只有O(n)。(具体证明感觉有点复杂啊,意会吧)
#include<cstdio> char B[1<<25],*S=B,C;int X; inline int read() { while((C=*S++)<'0'||C>'9'); for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0'; return X; } #define MN 4000000 int a[MN+5]; int main() { freopen("minecraft.in","r",stdin); freopen("minecraft.out","w",stdout); fread(B,1,1<<25,stdin); int n=read(),i,j,k; for(i=0;i<n;++i)a[i]=a[i+n]=read(); for(i=0,j=1;j<n;++j) { for(k=0;a[i+k]==a[j+k];++k); if(a[i+k]<a[j+k])i=j;else j+=k; } for(k=0;k<n;++k)printf("%d ",a[i+k]); fclose(stdin);fclose(stdout);return 0; }
T2.BBS叠五三
题目大意:给出一个长度为n的序列,可以删掉任意个元素,求出最多能有多少个a[i]满足a[i]=i。(n<=100,000)
我的做法:容易得到方程f[i]=f[j]+1 (j<i,a[j]<a[i],a[i]-a[j]<=i-j),发现f[i]若要从f[j]转移,则i-a[i]>=j-a[j],我们把f[i]存到dp[i-a[i]]里,按a[i]大小顺序dp即可,线段树之类的加速,复杂度O(nlogn)。
正解:考虑方程转移的三个条件,若后两个满足则第一个也满足,按后两个其中一个条件排序后做最长上升/不降子序列即可。(仔细想了想好像跟我的做法本质相同啊)
#include<cstdio> #include<cstring> #include<vector> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 100000 #define N 262144 vector<int> v[MN+5]; int a[MN+5],t[N*2+5]; void renew(int k,int z){for(k+=N+1;k;k>>=1)t[k]=max(t[k],z);} int query(int l,int r) { int res=t[0]; for(l+=N,r+=N+2;l^r^1;l>>=1,r>>=1) { if(~l&1)res=max(res,t[l+1]); if( r&1)res=max(res,t[r-1]); } return res; } int main() { freopen("fivethree.in","r",stdin); freopen("fivethree.out","w",stdout); int n=read(),i,j; for(i=1;i<=n;++i){j=read();if(j<=n)v[j].push_back(i);} memset(t,128,sizeof(t)); renew(n,0); for(i=1;i<=n;++i)for(j=v[i].size();j--;) renew(v[i][j]+n-i,query(0,v[i][j]+n-i)+1); printf("%d",query(0,n<<1)); fclose(stdin);fclose(stdout);return 0; }
T3.BBS复仇
题目大意:一张n个点m条边的图,某人要从1号点走到n号点,途中要经过2~k+1号点,并且要经过的这k个点有依赖关系,必须经过过被依赖的点再经过有依赖的点才算有效,求最短路。(n<=50000,m<=200000,k<=20)
我的做法:因为机子突然奇怪的自动重启好几次,最后没来得及打。
正解:做法比较显然,对1~k+1号点都跑一发单源最短路,然后随便状压DP,复杂度O(k*最短路(n,m)+k^2*2^k)。
T4.BBS开锁
题目大意:一个长度为n的序列,找出一个区间,选出区间中的任意一个数与该区间的次大值异或,求出最大的异或值。(n<=50000,1<=序列元素<=10^9)
思路:枚举数a[i]作为次大值,a[i]能成为次大值的条件是区间左边或右边有且只有一个比它大的数,二分这个数向左第一个比它大的数a[l1],第二个比它大的数a[l2],右边同样二分出r1和r2,我们只要统计区间(l2,r1),(l1,r2)中异或上a[i]的最大值,用可持久化Trie树前缀和一下就能搞了,二分查询的时候可以使用ST表,总复杂度O(nlogn)。
正解:从大到小把元素插入一个set来代替上面说的那个二分。
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 50000 #define N 65536 #define MK 15 #define K 30 #define ND 1550000 int a[MN+5],mx[MK+1][MN+5],f[MN+5],rt[MN+5],tn; struct node{int c[2],s;}T[ND+5]; void ins(int x,int z) { for(int pl=rt[x-1],p=rt[x]=++tn,i=K,k;i--;) { k=bool(z&(1<<i)); T[p].c[k^1]=T[pl].c[k^1]; p=T[p].c[k]=++tn;pl=T[pl].c[k]; T[p].s=T[pl].s+1; } } int query(int l,int r,int z) { int i,k,res=0,pl=rt[l-1],p=rt[r]; for(i=K;i--;) { k=bool(z&(1<<i)); if(T[T[p].c[k^1]].s-T[T[pl].c[k^1]].s) res+=1<<i,p=T[p].c[k^1],pl=T[pl].c[k^1]; else p=T[p].c[k],pl=T[pl].c[k]; } return res; } int query(int l,int r) { int p=f[r-l+1]; return max(mx[p][l],mx[p][r-(1<<p)+1]); } int main() { freopen("lock.in","r",stdin); freopen("lock.out","w",stdout); int n=read(),i,j,l,r,mid,lp,rp,lp2,rp2,ans=0; for(i=1;i<=n;++i)mx[0][i]=a[i]=read(); for(i=1;i<=MK;++i)for(j=1;j+(1<<i)-1<=n;++j) mx[i][j]=max(mx[i-1][j],mx[i-1][j+(1<<i-1)]); for(i=1;i<=n;--f[i++])for(j=i;j;j>>=1)++f[i]; for(i=1;i<=n;++i)ins(i,a[i]); for(i=1;i<=n;++i) { for(lp=0,l=1,r=i;l<=r;) if(query(mid=l+r>>1,i)>a[i])lp=mid,l=mid+1; else r=mid-1; if(lp)for(lp2=0,l=1,r=lp-1;l<=r;) if(query(mid=l+r>>1,lp-1)>a[i])lp2=mid,l=mid+1; else r=mid-1; for(rp=0,l=i,r=n;l<=r;) if(query(i,mid=l+r>>1)>a[i])rp=mid,r=mid-1; else l=mid+1; if(rp)for(rp2=n+1,l=rp+1,r=n;l<=r;) if(query(rp+1,mid=l+r>>1)>a[i])rp2=mid,r=mid-1; else l=mid+1; if(lp&&rp)ans=max(ans,max(query(lp2+1,rp-1,a[i]),query(lp+1,rp2-1,a[i]))); else if(lp)ans=max(ans,query(lp2+1,n,a[i])); else if(rp)ans=max(ans,query(1,rp2-1,a[i])); } printf("%d",ans); fclose(stdin);fclose(stdout);return 0; }