20190801考试反思
这次考试可以说是 极其失败。首先在下午一开始拿到题,头脑不清,夜不能寐,辗转反侧,积劳成疾,胡思乱想。看一眼T1,????学长讲过?我又没做???完了完了,好像是什么二分建树,先打个暴力再说,这个暴力挺好打,打完过样例20min,看T2:?????????方案数??完了图论方案数,心里阴影「奇怪的道路」。先打个暴力再说,dfs一波,打完40min,看T3:????????位运算,异或???手动吃屎。先……打个暴力?枚举一波,打完60min,一看表,好吧开始想正解吧。想了一会200min。。。。收卷。然后获得了出题人有手就送的暴力分。想了两个半小时以后啥也没想出来。。。
T1:线段树维护桶排序,然后26次赋值。。。手动排序,理论复杂度$O(mlogn)$极其友好,然而可见常数就有$26^{2}$,而且教练不给开氧,于是我们就各种神仙操作,其中最有效的是26次循环展开,代码令人心疼
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int N=100020; char op[N]; int bin[30]; struct node { int tin[30]; int &operator [](int x){return tin[x];} node operator +(node b) { node c;c.clear(); for(int i=0;i<26;i++) c[i]=tin[i]+b[i]; return c; } void clear(){memset(tin,0,sizeof(tin));} void print() { for(int i=0;i<26;i++) printf("%d %d\n",i,tin[i]); puts(""); } }; struct tree{int l,r,w,f;node bin;}tr[N*4]; inline int rd() { int s=0,w=1; char cc=getchar(); while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();} while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); return s*w; } void change(int k,int w) { tr[k].w=(tr[k].r-tr[k].l+1)*w; tr[k].f=w; tr[k].bin[0]=0; tr[k].bin[1]=0; tr[k].bin[2]=0; tr[k].bin[3]=0; tr[k].bin[4]=0; tr[k].bin[5]=0; tr[k].bin[6]=0; tr[k].bin[7]=0; tr[k].bin[8]=0; tr[k].bin[9]=0; tr[k].bin[10]=0; tr[k].bin[11]=0; tr[k].bin[12]=0; tr[k].bin[13]=0; tr[k].bin[14]=0; tr[k].bin[15]=0; tr[k].bin[16]=0; tr[k].bin[17]=0; tr[k].bin[18]=0; tr[k].bin[19]=0; tr[k].bin[20]=0; tr[k].bin[21]=0; tr[k].bin[22]=0; tr[k].bin[23]=0; tr[k].bin[24]=0; tr[k].bin[25]=0; tr[k].bin[w]=tr[k].r-tr[k].l+1; } void updata(int k) { tr[k].w=tr[k<<1].w+tr[k<<1|1].w; tr[k].bin[0]=tr[k<<1].bin[0]+tr[k<<1|1].bin[0]; tr[k].bin[1]=tr[k<<1].bin[1]+tr[k<<1|1].bin[1]; tr[k].bin[2]=tr[k<<1].bin[2]+tr[k<<1|1].bin[2]; tr[k].bin[3]=tr[k<<1].bin[3]+tr[k<<1|1].bin[3]; tr[k].bin[4]=tr[k<<1].bin[4]+tr[k<<1|1].bin[4]; tr[k].bin[5]=tr[k<<1].bin[5]+tr[k<<1|1].bin[5]; tr[k].bin[6]=tr[k<<1].bin[6]+tr[k<<1|1].bin[6]; tr[k].bin[7]=tr[k<<1].bin[7]+tr[k<<1|1].bin[7]; tr[k].bin[8]=tr[k<<1].bin[8]+tr[k<<1|1].bin[8]; tr[k].bin[9]=tr[k<<1].bin[9]+tr[k<<1|1].bin[9]; tr[k].bin[10]=tr[k<<1].bin[10]+tr[k<<1|1].bin[10]; tr[k].bin[11]=tr[k<<1].bin[11]+tr[k<<1|1].bin[11]; tr[k].bin[12]=tr[k<<1].bin[12]+tr[k<<1|1].bin[12]; tr[k].bin[13]=tr[k<<1].bin[13]+tr[k<<1|1].bin[13]; tr[k].bin[14]=tr[k<<1].bin[14]+tr[k<<1|1].bin[14]; tr[k].bin[15]=tr[k<<1].bin[15]+tr[k<<1|1].bin[15]; tr[k].bin[16]=tr[k<<1].bin[16]+tr[k<<1|1].bin[16]; tr[k].bin[17]=tr[k<<1].bin[17]+tr[k<<1|1].bin[17]; tr[k].bin[18]=tr[k<<1].bin[18]+tr[k<<1|1].bin[18]; tr[k].bin[19]=tr[k<<1].bin[19]+tr[k<<1|1].bin[19]; tr[k].bin[20]=tr[k<<1].bin[20]+tr[k<<1|1].bin[20]; tr[k].bin[21]=tr[k<<1].bin[21]+tr[k<<1|1].bin[21]; tr[k].bin[22]=tr[k<<1].bin[22]+tr[k<<1|1].bin[22]; tr[k].bin[23]=tr[k<<1].bin[23]+tr[k<<1|1].bin[23]; tr[k].bin[24]=tr[k<<1].bin[24]+tr[k<<1|1].bin[24]; tr[k].bin[25]=tr[k<<1].bin[25]+tr[k<<1|1].bin[25]; } void down(int k) { change(k<<1,tr[k].f); change(k<<1|1,tr[k].f); tr[k].f=-1; } void build(int k,int l,int r) { tr[k].l=l,tr[k].r=r; if(l==r){tr[k].w=op[l]-'a';tr[k].bin[tr[k].w]++;return ;} int mid=l+r>>1; tr[k].f=-1; tr[k].bin.clear(); build(k<<1,l,mid);build(k<<1|1,mid+1,r); updata(k); } int ask(int k,int id) { int l=tr[k].l,r=tr[k].r,mid=l+r>>1; if(l==r) return tr[k].w; if(tr[k].f!=-1) down(k); if(id<=mid) return ask(k<<1,id); else return ask(k<<1|1,id); } node query(int k,int x,int y) { int l=tr[k].l,r=tr[k].r,mid=l+r>>1; if(l==x&&r==y) return tr[k].bin; if(tr[k].f!=-1) down(k); if(y<=mid) return query(k<<1,x,y); else if(x>mid) return query(k<<1|1,x,y); return query(k<<1,x,mid)+query(k<<1|1,mid+1,y); } void add(int k,int x,int y,int w) { int l=tr[k].l,r=tr[k].r,mid=l+r>>1; if(x==l&&r==y){change(k,w);return ;} if(tr[k].f!=-1) down(k); if(y<=mid) add(k<<1,x,y,w); else if(x>mid) add(k<<1|1,x,y,w); else add(k<<1,x,mid,w),add(k<<1|1,mid+1,y,w); updata(k); } inline void binsort(int l,int r,int x) { node c=query(1,l,r); int k=l; for(int i=0;i<26;i++)bin[i]=c[i]; if(x) { if(bin[0]){add(1,k,k+bin[0]-1,0);k+=bin[0];} if(bin[1]){add(1,k,k+bin[1]-1,1);k+=bin[1];} if(bin[2]){add(1,k,k+bin[2]-1,2);k+=bin[2];} if(bin[3]){add(1,k,k+bin[3]-1,3);k+=bin[3];} if(bin[4]){add(1,k,k+bin[4]-1,4);k+=bin[4];} if(bin[5]){add(1,k,k+bin[5]-1,5);k+=bin[5];} if(bin[6]){add(1,k,k+bin[6]-1,6);k+=bin[6];} if(bin[7]){add(1,k,k+bin[7]-1,7);k+=bin[7];} if(bin[8]){add(1,k,k+bin[8]-1,8);k+=bin[8];} if(bin[9]){add(1,k,k+bin[9]-1,9);k+=bin[9];} if(bin[10]){add(1,k,k+bin[10]-1,10);k+=bin[10];} if(bin[11]){add(1,k,k+bin[11]-1,11);k+=bin[11];} if(bin[12]){add(1,k,k+bin[12]-1,12);k+=bin[12];} if(bin[13]){add(1,k,k+bin[13]-1,13);k+=bin[13];} if(bin[14]){add(1,k,k+bin[14]-1,14);k+=bin[14];} if(bin[15]){add(1,k,k+bin[15]-1,15);k+=bin[15];} if(bin[16]){add(1,k,k+bin[16]-1,16);k+=bin[16];} if(bin[17]){add(1,k,k+bin[17]-1,17);k+=bin[17];} if(bin[18]){add(1,k,k+bin[18]-1,18);k+=bin[18];} if(bin[19]){add(1,k,k+bin[19]-1,19);k+=bin[19];} if(bin[20]){add(1,k,k+bin[20]-1,20);k+=bin[20];} if(bin[21]){add(1,k,k+bin[21]-1,21);k+=bin[21];} if(bin[22]){add(1,k,k+bin[22]-1,22);k+=bin[22];} if(bin[23]){add(1,k,k+bin[23]-1,23);k+=bin[23];} if(bin[24]){add(1,k,k+bin[24]-1,24);k+=bin[24];} if(bin[25]){add(1,k,k+bin[25]-1,25);k+=bin[25];} } else { if(bin[25]){add(1,k,k+bin[25]-1,25);k+=bin[25];} if(bin[24]){add(1,k,k+bin[24]-1,24);k+=bin[24];} if(bin[23]){add(1,k,k+bin[23]-1,23);k+=bin[23];} if(bin[22]){add(1,k,k+bin[22]-1,22);k+=bin[22];} if(bin[21]){add(1,k,k+bin[21]-1,21);k+=bin[21];} if(bin[20]){add(1,k,k+bin[20]-1,20);k+=bin[20];} if(bin[19]){add(1,k,k+bin[19]-1,19);k+=bin[19];} if(bin[18]){add(1,k,k+bin[18]-1,18);k+=bin[18];} if(bin[17]){add(1,k,k+bin[17]-1,17);k+=bin[17];} if(bin[16]){add(1,k,k+bin[16]-1,16);k+=bin[16];} if(bin[15]){add(1,k,k+bin[15]-1,15);k+=bin[15];} if(bin[14]){add(1,k,k+bin[14]-1,14);k+=bin[14];} if(bin[13]){add(1,k,k+bin[13]-1,13);k+=bin[13];} if(bin[12]){add(1,k,k+bin[12]-1,12);k+=bin[12];} if(bin[11]){add(1,k,k+bin[11]-1,11);k+=bin[11];} if(bin[10]){add(1,k,k+bin[10]-1,10);k+=bin[10];} if(bin[9]){add(1,k,k+bin[9]-1,9);k+=bin[9];} if(bin[8]){add(1,k,k+bin[8]-1,8);k+=bin[8];} if(bin[7]){add(1,k,k+bin[7]-1,7);k+=bin[7];} if(bin[6]){add(1,k,k+bin[6]-1,6);k+=bin[6];} if(bin[5]){add(1,k,k+bin[5]-1,5);k+=bin[5];} if(bin[4]){add(1,k,k+bin[4]-1,4);k+=bin[4];} if(bin[3]){add(1,k,k+bin[3]-1,3);k+=bin[3];} if(bin[2]){add(1,k,k+bin[2]-1,2);k+=bin[2];} if(bin[1]){add(1,k,k+bin[1]-1,1);k+=bin[1];} if(bin[0]){add(1,k,k+bin[0]-1,0);k+=bin[0];} } } int main() { int n=rd(),m=rd(); scanf("%s",op+1); build(1,1,n); for(int i=1;i<=m;++i) { int l=rd(),r=rd(),x=rd(); binsort(l,r,x); } for(int i=1;i<=n;++i) printf("%c",(char)ask(1,i)+'a'); puts(""); return 0; } /* g++ 1.cpp -o 1 ./1 5 2 cabcd 1 3 1 3 5 0 */
T2:神仙题,状态定义令人发指。。。$f[i][j]$表示前i列有j列已经放入左端点在i以前的右区间,并且i列以前已经结束的左区间以及考虑完毕的方案数。。。。。突然想找到出题人打一顿。。。稍微感性理解一下,再定义几个已知量$l[i]$代表在右端点在1到i列的区间数,$r[i]$表示左端点在1到i列的右区间数,然后就可以得到转移,我还是觉得填表好理解,$r[i]-(j-1)$为目前剩余可填的右区间数,那么就可以知道$f[i][j]=f[i-1][j-1]*(r[i]-j+1)+f[i-1][j]$就是当前列放或不放,当然还得考虑当前左区间,接下来可能比较难以理解,我也搞了好一会,首先左右区间由是用乘法原理应该很能理解,那么就剩左区间的个数了,我的理解是:由于在左区间中长区间不一定会影响短区间(可能放在短区间之外)但短区间一定会影响长区间,所以我从左往右扫就能从短到长,因此直接能求解,然而此时从右区间就是从长到短,所以我要DP求解,那么我们如何求左区间,刚刚说过短的一定影响长的(不要取笑……)我们还是已经处理完短的,那就直接排除影响找到能放的列$i-j-l[i-1]$就是此时能放的列,有$l[i]-l[i-1]$行要放,因此直接搞排列,因为是行列相交的那块,可以yy一下为啥不是组合数,$f[i][j]*=A_{i-j-l[i-1]}^{l[i]-l[i-1]}$,然后注意一下边界,不要有负数之类的,就结束了。
#include<iostream> #include<cstdio> using namespace std; const int N=3020,mod=998244353; int l[N],r[N],f[N][N],fac[N],inv[N]; int rd() { int s=0,w=1; char cc=getchar(); while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();} while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); return s*w; } int qpow(int a,int k) { int ans=1; for(;k;k>>=1,a=1ll*a*a%mod) if(k&1) ans=1ll*ans*a%mod; return ans; } int A(int n,int m) { if(n<0||m<0) return 0; if(n<m) return 0; if(m==0) return 1; return 1ll*fac[n]*inv[n-m]%mod; } int main() { // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); int n=rd(),m=rd();fac[0]=1; int tm=max(n,m); for(int i=1;i<=tm;i++) fac[i]=1ll*fac[i-1]*i%mod; inv[tm]=qpow(fac[tm],mod-2); for(int i=tm;i;i--) inv[i-1]=1ll*inv[i]*i%mod; for(int i=1,x,y;i<=n;i++) { x=rd(),y=rd(); l[x]++;r[y]++; } for(int i=1;i<=m;i++) r[i]+=r[i-1],l[i]+=l[i-1]; f[0][0]=1; for(int i=1;i<=m;i++) for(int j=0;j<=n;j++) { if(j==0) f[i][j]=(f[i][j]+f[i-1][j])%mod; else { if(r[i]-j+1>=0) f[i][j]=(f[i][j]+1ll*f[i-1][j-1]*(r[i]-j+1)%mod+f[i-1][j])%mod; else f[i][j]=(f[i][j]+f[i-1][j])%mod; } f[i][j]=1ll*f[i][j]*A(i-j-l[i-1],l[i]-l[i-1])%mod; } printf("%d\n",f[m][n]); return 0; } /* g++ 1.cpp -o 1 ./1 2 6 2 4 5 6 */
T3:我不写题解,先给吴神上柱香,四句话给我搞懂了。
//发现:对手的操作就是把目前数字左移1位,溢出位补到末尾 //转化:对手可以把前1~i个操作都左移1位,溢出同上 //简化:对手一共有m个数,他会选其一来异或你使你尽量小 //问题:选出数使它被m个数中任意一个异或后的得数的最小值尽量大,最大值?方案数? //求解:0/1-trie,尽量避开对手的m个数。复杂度O(mn)? #include<cstdio> int n,m,c[100005],ss,ans=-1,fa,cnt,t[3000005][2]; int ll(int p){return p&1<<n-1?p<<1^1<<n^1:p<<1;} void insert(int p,int pos){ for(int i=n-1;~i;--i) pos=t[pos][p&1<<i?1:0]?t[pos][p&1<<i?1:0]:(t[pos][p&1<<i?1:0]=++cnt); } void dfs(int pos,int aans,int dep){ if(dep==-1){ if(aans==ans)fa++; if(aans>ans)ans=aans,fa=1; return; } if(t[pos][0]&&t[pos][1])dfs(t[pos][0],aans,dep-1),dfs(t[pos][1],aans,dep-1); else if(t[pos][0])dfs(t[pos][0],aans|1<<dep,dep-1); else dfs(t[pos][1],aans|1<<dep,dep-1); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;++i)scanf("%d",&c[i]),ss^=c[i]; for(int i=0;i<=m;++i)ss^=c[i],ss^=ll(c[i]),insert(ss,0); dfs(0,0,n-1); printf("%d\n%d\n",ans,fa); }
努力成长