8.10题解
T1
这题竟然真是贪心,我xxxx,考试的时候本来想到贪心了,结果,我在代码上写了句话“//贪心不太对的样子,跳近一点多跳几个格子,可能可以给下一个青蛙留出落脚点”,我当时一定是脑子废掉了想那么多,然后这题我就一点头绪都没有了,在看了大概一个多小时什么都没打之后,我把它弃掉之后,在交卷前最后20多分钟随便打了个DP?结果RE0了
说说它的贪心吧,对于你每一只青蛙都尽量跳到它可以跳到的最远的地方,毕竟你如果不跳的最远的话,你占的石头就会变多,给后面的青蛙留下的石头就会更少,关于我那个zz的认为贪心不对的想法,我们来想一想,对于每一个青蛙来说他们的跳跃能力是一样的,如果1青蛙可以跳到的最远的点,恰巧是2青蛙能够到达的唯一一个点,只有一种可能情况,就是1青蛙能够跳到的最远的点是他前面第一个点,不然的话,只要1青蛙和最远点之间有一个点,2青蛙就可以落脚,然后就解决只有一个石头的情况,此时那块石头是1,2两只青蛙的最远点,那他俩就是你死我活的关系,所以贪心的正确性我们就yy严格的证明出来了,剩下的就跳跳跳就可以了,当然一个个跳,找最远点还是会T,所以我们需要一个神奇的$STL$:$set$,这样的话我们最终就以$O(nlogn)$的复杂度解决了这道题
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<set> 5 #define maxn 1001000 6 using namespace std; 7 int t; 8 int a[maxn]; 9 set <int> ss; 10 int main() 11 { 12 scanf("%d",&t); 13 while(t--) 14 { 15 int n,m,d,l,ans=0; ss.clear(); 16 scanf("%d%d%d%d",&n,&m,&d,&l); 17 a[1]=0; n++; ss.insert(0); 18 for(int i=2;i<=n;++i) 19 { 20 scanf("%d",&a[i]); 21 ss.insert(a[i]); 22 } 23 a[++n]=l; ss.insert(a[n]); 24 while(ss.size()>2) 25 { 26 int bj=0; 27 set<int>::iterator qd=ss.begin(); 28 while(*qd<a[n]) 29 { 30 set<int>::iterator ls=--ss.upper_bound(*qd+d); 31 if(*ls==a[n]) {ans++; break;} 32 if(*ls+d>=a[n]) {ss.erase(*ls); ans++; break;} 33 if(*ls<=*qd) {bj=1; break;} 34 qd=ls; ss.erase(*ls); 35 } 36 if(bj==1) break; 37 if(ans==m) break; 38 } 39 if(ans>=m) printf("Excited\n"); 40 else printf("%d\n",ans); 41 } 42 return 0; 43 }
T2
学长讲过的原题,我太废物了,不过他的线段树存的东西,我说实话考场想不到,但是其实emm,怎么说呢,想到的话其实还挺好维护的,先说一下维护什么
struct node{ int zuo,you;//正常线段树记录管辖范围 int ceng;//zuo到you这个区间中一共有多少层 ll sum;//zuo到you这么多层中一共有多少量的金坷垃 ll cut;//zuo到you这个区间中一共要删掉前面多少层 }a[maxm*4];
建树的时候叶子节点正常操作,然后就是给父亲上传节点信息时的$update$操作
void update(int f) { if(a[2*f+1].cut==0)//右儿子不删左儿子 { a[f].cut=a[2*f].cut; a[f].ceng=a[2*f].ceng+a[2*f+1].ceng; a[f].sum=a[2*f].sum+a[2*f+1].sum; } else if(a[2*f+1].cut>=a[2*f].ceng)//右儿子把左儿子删完了 { a[f].cut=a[2*f].cut+a[2*f+1].cut-a[2*f].ceng;//已经用左儿子抵消了右儿子的一部分cut a[f].ceng=a[2*f+1].ceng;//左儿子被删完了,无法作出贡献 a[f].sum=a[2*f+1].sum; } else//删不干净左儿子 { a[f].cut=a[2*f].cut;//右儿子的删除贡献被清干净了 a[f].ceng=a[2*f].ceng+a[2*f+1].ceng-a[2*f+1].cut;//左儿子被右儿子删掉一部分 a[f].sum=a[2*f+1].sum+cal(2*f,a[2*f+1].cut);//函数用于查询左儿子被右儿子删掉一部分之后还剩多少量 } }
接下来就是$update$函数中的$cal$函数,他实际上通过不停的询问儿子的处理,来计算右儿子删除操作对左儿子造成的影响
int cal(int f,int w)//当前节点被删除,先删当前节点的右儿子,不够删再去删左儿子 { //右儿子刚好够删,直接返回左儿子 if(a[2*f+1].ceng==w) return a[f].sum-a[2*f+1].sum; //右儿子不能被删完,就准备去删右儿子的右儿子 if(a[2*f+1].ceng>w) return a[f].sum-a[2*f+1].sum+cal(2*f+1,w); //右儿子不够删,带上删剩下的以及右儿子要删左儿子的,去删左儿子 return cal(2*f,w-a[2*f+1].ceng+a[2*f+1].cut); }
解释一下为什么左儿子不能直接写$sum$,而是用父节点-右儿子得到,因为左儿子实际上需要被右儿子删除,但当前你的左儿子中并没有计算右儿子的删除贡献,也就是说现在的左儿子的$sum$值并不真实
剩下的就是单点修改以及查询了
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<stack> 5 #define maxm 200100 6 #define ll long long 7 using namespace std; 8 struct node{ 9 int zuo,you,ceng; 10 ll sum,cut; 11 }a[maxm*4]; 12 int m,q; 13 int cal(int f,int w) 14 { 15 if(a[2*f+1].ceng==w) return a[f].sum-a[2*f+1].sum; 16 if(a[2*f+1].ceng>w) return a[f].sum-a[2*f+1].sum+cal(2*f+1,w); 17 return cal(2*f,w-a[2*f+1].ceng+a[2*f+1].cut); 18 } 19 void update(int f) 20 { 21 if(a[2*f+1].cut==0) 22 { 23 a[f].cut=a[2*f].cut; 24 a[f].ceng=a[2*f].ceng+a[2*f+1].ceng; 25 a[f].sum=a[2*f].sum+a[2*f+1].sum; 26 } 27 else if(a[2*f+1].cut>=a[2*f].ceng) 28 { 29 a[f].cut=a[2*f].cut+a[2*f+1].cut-a[2*f].ceng; 30 a[f].ceng=a[2*f+1].ceng; 31 a[f].sum=a[2*f+1].sum; 32 } 33 else 34 { 35 a[f].cut=a[2*f].cut; 36 a[f].ceng=a[2*f].ceng+a[2*f+1].ceng-a[2*f+1].cut; 37 a[f].sum=a[2*f+1].sum+cal(2*f,a[2*f+1].cut); 38 } 39 } 40 void build(int f,int l,int r) 41 { 42 a[f].zuo=l; a[f].you=r; 43 if(l==r) 44 { 45 int k; ll v; 46 scanf("%d%lld",&k,&v); 47 if(k==0) {a[f].sum=v; a[f].ceng=1;} 48 else a[f].cut=v; 49 return ; 50 } 51 int mid=(l+r)>>1; 52 build(2*f,l,mid); build(2*f+1,mid+1,r); 53 update(f); 54 } 55 void change(int f,int d,int opt,ll w) 56 { 57 if(a[f].zuo==a[f].you) 58 { 59 if(opt==0) {a[f].cut=0; a[f].sum=w; a[f].ceng=1;} 60 else {a[f].sum=0; a[f].ceng=0; a[f].cut=w;} 61 return ; 62 } 63 int mid=(a[f].zuo+a[f].you)>>1; 64 if(d<=mid) change(2*f,d,opt,w); 65 else change(2*f+1,d,opt,w); 66 update(f); 67 } 68 int main() 69 { 70 scanf("%d%d",&m,&q); 71 build(1,1,m); 72 while(q--) 73 { 74 int c,k; ll v; scanf("%d%d%lld",&c,&k,&v); 75 change(1,c,k,v); 76 printf("%lld\n",a[1].sum); 77 } 78 return 0; 79 }
T3
T3数据过水,被我用30分的代码水过了,还没理解正解,先留坑,不过话说$getchar$和$putchar$也太快了点吧,整整帮我卡掉3000毫
暴力就他让干什么就干什么,直接模拟就可以了,我大概思考了一下,复杂度应该是$O(qn^2)$的样子,跑了11000过了
1 //里层也需要翻 2 #include<cstdio> 3 #include<iostream> 4 #define maxn 2100 5 using namespace std; 6 int n,m,q; 7 char ch; 8 char hs[maxn],hx[maxn],lz[maxn],ly[maxn]; 9 char a[maxn][maxn]; 10 int main() 11 { 12 scanf("%d%d%d",&n,&m,&q); 13 for(int i=1;i<=n;++i) 14 for(int j=1;j<=m;++j) 15 { 16 ch=getchar(); 17 while(ch<'0'||ch>'9') ch=getchar(); 18 while(ch>='0'&&ch<='9') {a[i][j]=ch; ch=getchar();} 19 } 20 while(q--) 21 { 22 int x,y,len,i,j; scanf("%d%d%d",&x,&y,&len); 23 while(len>=1) 24 { 25 for(i=y;i<=y+len-1;++i) {hs[i]=a[x][i]; hx[i]=a[x+len-1][i];} 26 for(i=x;i<=x+len-1;++i) {lz[i]=a[i][y]; ly[i]=a[i][y+len-1];} 27 for(i=x,j=y+len-1;i<=x+len-1,j>=y;++i,--j) a[x][j]=lz[i]; 28 for(i=y,j=x;i<=y+len-1,j<=x+len-1;++i,++j) a[j][y]=hx[i]; 29 for(i=x+len-1,j=y;i>=x,j<=y+len-1;--i,++j) a[x+len-1][j]=ly[i]; 30 for(i=y+len-1,j=x+len-1;i>=y,j>=x;--i,--j) a[j][y+len-1]=hs[i]; 31 x++; y++; len-=2; 32 } 33 } 34 for(int i=1;i<=n;++i) 35 { 36 for(int j=1;j<=m;++j) {putchar(a[i][j]); putchar(' ');} 37 puts(""); 38 } 39 return 0; 40 }