T1 Blue
贪心,每次跳得时候跳能跳到的最远的地方,跳过的就把他设为0,每次二分找到位置,一直跳就行,如果能跳到的位置就是当前位置或比当前位置还小(数组里现在呆着的这一块石头,二分得到的就是当前位置,-1就比当前位置小了。但由于0的影响,while回退很慢,所以改用支持earse操作的set,可以水过他(这是T40和AC的区别!!!)
#include<iostream> #include<cstdio> #include<cstring> #include<set> #include<algorithm> using namespace std; int T,n,m,d,l; set<int>st; set<int>::iterator it; int read() { int aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=aa*10+cc-'0';cc=getchar();} return aa*bb; } int main() { T=read(); while(T--){ st.clear(); n=read();m=read();d=read(),l=read(); bool flag=0,flag1=0; for(int i=1;i<=n;i++){ st.insert(read()); } st.insert(l);st.insert(0); if(d==l){ puts("Excited"); continue; } for(int i=1;i<=m;i++){ int pos=0; while(pos<l){ if(pos+d>=l) break; it=st.upper_bound(pos+d); if(*(--it)<=pos){ flag=1; break; } pos=*it; st.erase(it); } if(flag){ printf("%d\n",i-1); flag1=1; break; } } if(flag1) continue; puts("Excited"); } return 0; }
T2 Weed
树袋熊学长的课件里讲过,然而我并不会。
又是一道神奇的线段树,用一个神奇的cal函数,我们就可以愉快的拿到100分的好成绩!
以时间为下标,建一棵线段树,树中维护4个值sum,del,cnt,las
sum:当前区间内所有操作添 删之后剩下金克拉的总和 ans=t[1].sum
del:当前区间还要向前(当前区间的前一个区间)删除多少层
cnt:当前区间被自己区间删完后还剩多少层
las:左儿子被右儿子用del删除后还剩多少(只对左儿子维护)
cal函数的作用就是计算las的值,update的时候维护x信息时,左儿子会被右儿子删除,为了维护区间信息的正确性,需要用右儿子的del删除左儿子的一些值,删除等操作我们只是保存在区间信息里,而不对下面的节点做修改(因为这是区间信息)。左儿子的cnt和右儿子的del有下面几种关系,分别处理即可
1.l.cnt<r.del 左儿子不够删,那就直接当作左儿子不存在,计算剩下的值得到x的sum值
2.r.del==0 右儿子中不存在删除操作,直接合并
3.l.r.cnt>r.del 左儿子的右儿子就够右儿子删了,return l.las+cal(l.r,r.del)
4.l.r.cnt<r.del 左儿子的右儿子不够右儿子不够删,那就继续去左儿子的左儿子里删 return cal(l.l,r.del-l.r.cnt+l.r.del) 记得加上左儿子的右儿子对左儿子的左儿子的del
5.l.r.cnt==r.del 左儿子的右儿子刚好够删,直接return l.las
修改操作实际上就是线段树的单点修改操作,改动一个值,其他区间的信息会被update上去,最后t[1].sum即为答案。
/*sum:区间内删完后的总和 del:当前区间要往前删多少个 cnt:当前区间删完后还有多少个加的操作 las:左儿子被右儿子删完后的总和*/ #include<iostream> #include<cstring> #include<cstdio> using namespace std; struct node { int l,r,sum,del,cnt,las; }t[800100]; int n,m,opt,v; int read() { int aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=aa*10+cc-'0';cc=getchar();} return aa*bb; } int cal(int x,int num) { if(t[x*2+1].cnt>num) return t[x*2].las+cal(x*2+1,num); if(t[x*2+1].cnt<num) return cal(x*2,num-t[x*2+1].cnt+t[x*2+1].del); if(t[x*2+1].cnt==num) return t[x*2].las; } void update(int x) { if(!t[x*2+1].del){ t[x].sum=t[x*2].sum+t[x*2+1].sum; t[x].del=t[x*2].del; t[x].cnt=t[x*2].cnt+t[x*2+1].cnt; t[x*2].las=t[x*2].sum; return; } if(t[x*2].cnt<=t[x*2+1].del){ t[x].sum=t[x*2+1].sum; t[x].del=t[x*2].del+t[x*2+1].del-t[x*2].cnt; t[x].cnt=t[x*2+1].cnt; t[x*2].las=0; return; } t[x*2].las=cal(x*2,t[x*2+1].del); t[x].sum=t[x*2].las+t[x*2+1].sum; t[x].del=t[x*2].del; t[x].cnt=t[x*2+1].cnt+t[x*2].cnt-t[x*2+1].del; } void build(int x,int l,int r) { t[x].l=l;t[x].r=r; if(l==r){ opt=read(); if(opt==0){ t[x].sum=read(); t[x].del=0; t[x].cnt=1; t[x].las=0; } else{ t[x].sum=0; t[x].del=read(); t[x].cnt=0; t[x].las=0; } return; } int mid=(l+r)>>1; build(x*2,l,mid); build(x*2+1,mid+1,r); update(x); } void change(int x,int pos) { if(t[x].l==t[x].r&&t[x].l==pos){ if(opt==0){ t[x].sum=v; t[x].del=0; t[x].cnt=1; t[x].las=0; } else{ t[x].sum=0; t[x].del=v; t[x].cnt=0; t[x].las=0; } return; } int mid=(t[x].l+t[x].r)>>1; if(pos<=mid) change(x*2,pos); else change(x*2+1,pos); update(x); } int main() { n=read();m=read(); build(1,1,n); for(int i=1,j;i<=m;i++){ j=read();opt=read();v=read(); change(1,j); printf("%d\n",t[1].sum); } return 0; }
T3 Drink
一圈一圈的先存下来,然后转后应在的地方“铺”上去,用队列存T30,用数组存并且用char类型存储表格(只有1~9)可以到T60,调整一下循环顺序T80,register删掉,for改while可以卡到T90,最后把快读稍改,就A了
就一句话,疯狂卡常可以A。
然而正解并不长这样。。。具体长啥样我也不知道
以下是正解:
Drink:
看惯了罗马音的小朋友们都会知道r发l的音,题目名:D Link.
每次修改都会改变O( N ^ 2 )个位置的值,二维平面上没有一个确定的正方向,因此也无法使用传统数据结构。
什么东西的变化量是O( N )级别的呢?
如果把每个点都看作一个人,他的头面向某个方向。分别记录这个人的方向上的前后左右分别是谁,那么每次旋转改变的只是正方形边缘上的值,以及所有点的方向。
这样来看至少我们发现了变化量为O( N )级别的东西啦(虽然方向的该变量还是O( N ^ 2 ))。
注意到,我们并不需要真的知道每个点的方向,我们只需要正确地维护前后左右四个值即可。因为每个点的方向是可以由已知方向的相邻点算出来的!
每个点确实是有方向的,但是我们不用纪录也不用直接更改它,每次只要正确修改边界上的值,整个矩形的方向就会自动改变。
解决啦!
复杂度O( Q * N ), 常数稍大。
#include<iostream> #include<cstdio> const int L=1<<20|1; char buffer[L],*S,*T; #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) using namespace std; char a[2010][2010],c,b[8010]; int read() { int aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();} return aa*bb; } int main() { int n,m,qq,num; n=read(),m=read(),qq=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ c=getchar(); while(c<'0'||c>'9') c=getchar(); a[i][j]=c; } int x,y,l,i=1,j; while(i<=qq){ x=read();y=read();l=read(); while(l>=2){ num=0; j=0;while(j<l) b[++num]=a[x][y+j],++j; j=1;while(j<l) b[++num]=a[x+j][y+l-1],++j; j=l-2;while(j>=0) b[++num]=a[x+l-1][y+j],--j; j=l-2;while(j>=1) b[++num]=a[x+j][y],--j; j=l-2;while(j>=1) a[x][y+j]=b[num--],--j; j=0;while(j<=l-2) a[x+j][y]=b[num--],++j; j=0;while(j<=l-2) a[x+l-1][y+j]=b[num--],++j; j=l-1;while(j>=0) a[x+j][y+l-1]=b[num--],--j; x++,y++;l-=2; } i++; } i=1; while(i<=n){ j=1; while(j<=m){ putchar(a[i][j]),putchar(' '); j++; } puts(""); i++; } return 0; }
总之这道题的测试点还是很水的,T1T3暴力都可以水过,但样例给的太灭绝人性,把电脑卡崩3 4次。