2020 CSP-J复赛题解
身为一名高中生,却还是不知廉耻地做了一遍普及组的题目,体验一把AK的感觉
T1 优秀的拆分
T1还是一如既往的水。
根据题意,奇数直接-1,偶数就从一个很大的2的幂开始枚举,n比这个数大就输出这个数并且n减去这个数,然后这个数/2。
#include<iostream> using namespace std; int n; int main() { cin>>n; if(n&1){ cout<<-1; return 0; } for(int i=(1<<30);i>0;i/=2){ if(n>=i){ cout<<i<<" "; n-=i; } } return 0; }
T2 直播获奖
今年的T2还是比较难的,用到了两个大根堆。
建议先看看这两个题:
再看这个题就很板子了。
计算出小根堆的大小,然后不断push和pop操作即可。
#include<iostream> #include<queue> using namespace std; priority_queue<int> q1; priority_queue<int,vector<int>,greater<int> > q2; int n,w,a[100005]; int main(){ cin>>n>>w; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++){ int k=max(1,i*w/100); if(q2.empty()||q2.top()<=a[i]) q2.push(a[i]); else q1.push(a[i]); while(!q2.empty()&&q2.size()>k){ q1.push(q2.top()); q2.pop(); } while(q2.empty()||q2.size()<k){ q2.push(q1.top()); q1.pop(); } cout<<q2.top()<<" "; } return 0; }
T3 表达式
这个题就比较毒瘤。不但码量大,而且规律比较不明显。
后缀表达式用栈处理,不会的可以看看这个板子:P1449 后缀表达式
这个题先O(n)求出没取反前的答案,然后对于这q个询问,很显然不能每个询问改变一下再求,所以我们只能去寻找规律,做到O(1)出答案。
我们不难发现,对于每一个操作符,不管操作数是什么,答案最终只可能是0或者1,我们就可以记录下每一个操作符所对应的结果,并且记录这个结果是有哪两个操作数得来的,和这个结果所影响的下一步操作的编号是什么。
存在一个结构体里,即下标表示编号,id表示操作符类型,to表示影响的下一个操作的编号,value表示这一步的值,x1、x2表示两个操作数(!运算只用到x1)。
然后对于每一次修改,类似记忆化搜索,递归判断对答案有没有影响,把判断出来的用vis数组标记一下,即记下变换之后对下一个操作的答案没有影响。
因为我太菜了,所以一开始的string用了非常笨的方法处理。
#include<iostream> #include<cmath> #include<algorithm> #include<stack> #include<cstring> #include<cstdio> using namespace std; const int maxn=1000005; struct node{ int id; int to; int value; int x1,x2; }c[maxn*2]; int a[maxn],n,q; stack<int> s; string ss; int cnt=1000000; int vis[maxn*2],ans; int sss(string ss){ int n=0; for(int i=1;i<ss.length();i++) n+=(pow(10,ss.length()-i-1)*(ss[i]-'0')); return n; } int ssss(string ss){ int n=0; for(int i=0;i<ss.length();i++) n+=(pow(10,ss.length()-i-1)*(ss[i]-'0')); return n; } int dfs(int num){ if(c[num].id==0) return c[num].value=a[num]; if(c[num].id==1){ int x=dfs(c[num].x1),y=dfs(c[num].x2); if(x==1&&y==1) return c[num].value=1; else return c[num].value=0; } if(c[num].id==2){ int x=dfs(c[num].x1),y=dfs(c[num].x2); if(x==0&&y==0) return c[num].value=0; else return c[num].value=1; } if(c[num].id==3){ int x=dfs(c[num].x1); if(x==0) return c[num].value=1; else return c[num].value=0; } } bool query(int num){ if(vis[num]==1) return 1; if(vis[num]==2) return 0; int too=c[num].to; int x=c[too].x1,y=c[too].x2; if(x==num) x=y; if(c[num].value==0){ if(c[too].id==1&&c[x].value==1){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } if(c[too].id==1&&c[x].value!=1) return vis[num]=1; if(c[too].id==2&&c[too].value==0){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } if(c[too].id==2&&c[too].value==1) return vis[num]=1; if(c[too].id==3){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } } if(c[num].value==1){ if(c[too].id==1&&c[x].value==1){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } if(c[too].id==1&&c[x].value!=1) return vis[num]=1; if(c[too].id==2&&c[x].value==0){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } if(c[too].id==2&&c[x].value!=0) return vis[num]=1; if(c[too].id==3){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } } if(c[num].value>1){ if(c[too].id==1) return vis[num]=1; if(c[too].id==2&&c[x].value==0){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } if(c[too].id==2&&c[x].value!=0) return vis[num]=1; if(c[too].id==3){ query(too)?vis[num]=1:vis[num]=2; return vis[num]==1?1:0; } } } int main(){ while(1){ cin>>ss; if(ss[0]=='x') s.push(sss(ss)); else{ if('0'<=ss[0]&&ss[0]<='9'){ n=ssss(ss); break; } else{ if(ss[0]=='&'){ c[++cnt].id=1; c[s.top()].to=cnt; c[cnt].x1=s.top(); s.pop(); c[s.top()].to=cnt; c[cnt].x2=s.top(); s.pop(); s.push(cnt); }else{ if(ss[0]=='|'){ c[++cnt].id=2; c[s.top()].to=cnt; c[cnt].x1=s.top(); s.pop(); c[s.top()].to=cnt; c[cnt].x2=s.top(); s.pop(); s.push(cnt); }else{ if(ss[0]=='!'){ c[++cnt].id=3; c[s.top()].to=cnt; c[cnt].x1=s.top(); s.pop(); s.push(cnt); } } } } } } for(int i=1;i<=n;i++) scanf("%d",&a[i]); ans=dfs(cnt); if(n==1&&cnt==1000000){ cout<<a[1]; return 0; } cin>>q; vis[cnt]=2; for(int i=1;i<=q;i++){ int qu; scanf("%d",&qu); if(query(qu)) printf("%d\n",ans); else printf("%d\n",!ans); } return 0; }
T4 方格取数
这个题就是一个比较偏板子的dp。
用dp[i][j][0/1/2]分别表示 到第i行 第j列这个点 ,这一列从上往下/从下往上/没有约束 的最大值。
Q:为什么没有后效性呢?
A:因为不能往左走,所以可以一列一列处理,所以先枚举列。
然后就是一顿转移即可。
注意103*103*104=1010,会爆int!要用long long!
我TM提高组因为没开long longT2炸了,现在又忘了……
十年oi一场空,不开long long见祖宗。
#include<iostream> #include<cstdio> using namespace std; const int maxn=1005; int n,m,a[maxn][maxn]; long long dp[maxn][maxn][3]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&a[i][j]); } } for(int i=1;i<=n;i++){ dp[i][1][2]=dp[i-1][1][2]+a[i][1]; } for(int j=2;j<=m;j++){ dp[1][j][0]=dp[1][j-1][2]+a[1][j]; for(int i=2;i<=n;i++){ dp[i][j][0]=max(dp[i][j-1][2],dp[i-1][j][0])+a[i][j]; } dp[n][j][1]=dp[n][j-1][2]+a[n][j]; for(int i=n-1;i>=1;i--){ dp[i][j][1]=max(dp[i][j-1][2],dp[i+1][j][1])+a[i][j]; } for(int i=1;i<=n;i++){ dp[i][j][2]=max(dp[i][j][0],dp[i][j][1]); } } cout<<dp[n][m][2]; return 0; }
//期中考试炸了呜呜呜