[NOIP模拟16]题解
A.Blue
出题人大概已经去为国家处理积压子弹了?
贪心,让每一只青蛙(我怂行吧)都尽量往远跳,能到达的最远的被踩了就跳次远的,以此类推。可以维护一个单调队列,表示每只青蛙的位置(开始都是0)。然后按顺序扫一遍每个石头,如果队首的青蛙不能跳过去就放弃它直接pop掉,如果能跳就把石头位置从队尾push进去并pop掉队首的旧位置,最后队列的size就是答案。因为你肯定不能可着一个青蛙往前跳,必须尽量不让任何一只掉队,所以一只跳完之后把它丢到队尾。
比较神奇的是这道题用STL会比手写队列快300ms??
#include<cstdio> #include<iostream> #include<cstring> #include<queue> using namespace std; const int N=1e6+5; int T,n,m,D,L; int a[N]; void work() { cin>>n>>m>>D>>L; for(int i=1;i<=n;i++) cin>>a[i]; a[n+1]=L; deque<int> q; for(int i=1;i<=m;i++)q.push_back(0); for(int i=1;i<=n+1;i++) { while(!q.empty()&&q.front()+D<a[i])q.pop_front(); q.push_back(a[i]);q.pop_front(); } if(q.size()==m)puts("Excited"); else printf("%d\n",q.size()); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin>>T; while(T--)work(); return 0; }
#include<cstdio> #include<iostream> #include<cstring> #include<queue> using namespace std; const int N=1e6+5; int T,n,m,D,L; int a[N],q[N]; void work() { cin>>n>>m>>D>>L; for(int i=1;i<=n;i++) cin>>a[i]; a[n+1]=L; memset(q,0,sizeof(q)); int head=1,tail=m; for(int i=1;i<=n+1;i++) { while(head<=tail&&q[head]+D<a[i])head++; q[++tail]=a[i];head++; } if(tail-head+1==m)puts("Excited"); else printf("%d\n",tail-head+1); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin>>T; while(T--)work(); return 0; }
B.Weed
好题,先咕着
用线段树维护操作的题还是第一次见
线段树上的区间指的是一段连续的操作区间,而不是实际的序列
维护$add$(加了多少层)$del$(删了多少层)$sum$(有多少量)$lsum(被兄弟删除后的量,仅对左儿子维护)$
$up()$的时候如果左儿子不够右儿子删,直接清0即可。但要是右儿子删不完左儿子就很麻烦了。需要专门设计一个函数计算当前区间中删了x个元素的和。
(于是我又懒癌发作了)
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0',ch=getchar();} return x*f; } const int N=2e5+55; #define ls(k) k<<1 #define rs(k) k<<1|1 int m,Q; struct ques { int op,val; }q[N]; int del[N<<2],add[N<<2],lsum[N<<2],sum[N<<2]; int query(int k,int val) { if(val==add[rs(k)])return lsum[ls(k)]; if(val>add[rs(k)])return query(ls(k),val-add[rs(k)]+del[rs(k)]); if(val<add[rs(k)])return lsum[ls(k)]+query(rs(k),val); } void up(int k) { if(add[ls(k)]<=del[rs(k)]) { lsum[ls(k)]=0; sum[k]=sum[rs(k)]; add[k]=add[rs(k)]; del[k]=del[rs(k)]+del[ls(k)]-add[ls(k)]; } else { del[k]=del[ls(k)]; add[k]=add[ls(k)]+add[rs(k)]-del[rs(k)]; if(del[rs(k)])lsum[ls(k)]=query(ls(k),del[rs(k)]); else lsum[ls(k)]=sum[ls(k)]; sum[k]=sum[rs(k)]+lsum[ls(k)]; } } void build(int k,int l,int r) { if(l==r) { if(q[l].op==0)add[k]=1,sum[k]=lsum[k]=q[l].val; else del[k]=q[l].val; return ; } int mid=l+r>>1; build(ls(k),l,mid);build(rs(k),mid+1,r); up(k); } void update(int k,int l,int r,int pos,int op,int val) { if(l==r) { lsum[k]=add[k]=sum[k]=del[k]=0; if(op) del[k]=val; else add[k]=1,sum[k]=lsum[k]=val; return ; } int mid=l+r>>1; if(pos<=mid)update(ls(k),l,mid,pos,op,val); else update(rs(k),mid+1,r,pos,op,val); up(k); } int main() { /*freopen("weed.in","r",stdin); freopen("my.out","w",stdout);*/ m=read();Q=read(); for(int i=1;i<=m;i++) q[i].op=read(),q[i].val=read(); build(1,1,m); for(int i=1;i<=Q;i++) { int num=read(),op=read(),val=read(); update(1,1,m,num,op,val); printf("%d\n",sum[1]); } return 0; }
C.Drink
没有打恶心至极的大常数正解,其实优化暴力+卡常跑出来和正解的时间相差无几。
%%%WHS式循环展开 TQL 帅过WYS
优化暴力的思路就是每层矩形只枚举最上面的那条边上的点,因为有了这条边上一个点的坐标就可以完成矩形上四个点的旋转,所以只枚举一条边上的点就能完成整个矩形的旋转。
然后调整边长和左上角坐标,对每层矩形都来一遍就好了。复杂度其实相比最暴力的没有变,但是循环次数少了,相当于一种循环展开。
当然还要稍作卡常,$int$转$char$ $getchar$读入 $putchar$输出啥的
#include<cstdio> #include<iostream> #include<cstring> #define re register using namespace std; const int N=3005; int n,m,Q,x,y,c; char a[N][N],b[N][N]; inline int read() { re int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int main() { n=read();m=read();Q=read(); for(re int i=1;i<=n;i++) for(re int j=1;j<=m;j++) { a[i][j]=getchar(); while(!isdigit(a[i][j]))a[i][j]=getchar(); } while(Q--) { x=read();y=read();c=read(); for( ;c>1;c-=2,x++,y++) { re int L=y,U=x,D=x+c-1,R=y+c-1; for(re int i=0;i<=c-2;i++) { char old=a[U+i][R]; a[U+i][R]=a[U][L+i]; a[U][L+i]=a[D-i][L]; a[D-i][L]=a[D][R-i]; a[D][R-i]=old; } } } for(re int i=1;i<=n;i++) { for(re int j=1;j<=m;j++) putchar(a[i][j]),putchar(' '); putchar('\n'); } return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。