【考试题解】NOIP2024(欢乐)加赛3
A. Sakurako and Water
题目内容
给你一个
思路
有一个显然的性质:对于矩阵中任意一条主对角线进行操作,如果它还能再向左上或右下扩展,那么一定不如扩展到不能再扩展更优。这样,整个矩形只剩下了
代码
#include<bits/stdc++.h> using namespace std; #define il inline #define ri register int #define inf 0x3f3f3f3f int a,b,c[505][505],mx[1001]; long long ans; int main() { scanf("%d",&a); while(a--) { scanf("%d",&b); for(ri i=1;i<=b;i++) { for(ri j=b;j>=1;j--) { scanf("%d",&c[i][j]); } } memset(mx,0,sizeof(mx)); for(ri i=1;i<=b;i++) { for(ri j=1;j<=b;j++) { mx[i+j-1]=min(mx[i+j-1],c[i][j]); } } ans=0; for(ri i=1;i<=b<<1;i++) { ans-=mx[i]; } printf("%lld\n",ans); } return 0; }
B. Binomial Coefficients, Kind Of
题目内容
给你一个错误的递推求组合数的代码:
for(int n = 0; n < N; n++) { C[n][0] = 1; C[n][n] = 1; for(int k = 1; k < n; k++) C[n][k] = C[n][k-1] + C[n-1][k-1]; }
思路
打表找规律可以发现当
代码
#include<bits/stdc++.h> using namespace std; #define il inline #define ri register int #define inf 0x3f3f3f3f int a,b[100001],c[100001]; const int mod=1e9+7; long long jc[100001],ny[100001]; il long long qpow(long long x,long long y) { register long long rn=1; while(y) { if(y&1) { rn=(rn*x)%mod; } x=(x*x)%mod; y>>=1; } return rn; } il long long Cc(int x,int y) { if(!y||x==y) { return 1; } else { return qpow(2,y); } } int main() { scanf("%d",&a); for(ri i=1;i<=a;i++) { scanf("%d",&b[i]); } for(ri i=1;i<=a;i++) { scanf("%d",&c[i]); } for(ri i=1;i<=a;i++) { printf("%lld\n",Cc(b[i],c[i])); } return 0; }
C. QED's Favorite Permutation
题目内容
给定一个
思路
排列变有序,那么每个数该去的位置是好找的。如果所有该往左走的数都满足条件,那么整个序列也就可以变为有序的了。于是首先维护需要在哪些位置向左走,假设
代码
#include<bits/stdc++.h> using namespace std; #define il inline #define ri register int #define inf 0x3f3f3f3f int a,b,c,d[200002],u,can[200002],num; vector<pair<int,int>>vec; char e[200002]; int main() { scanf("%d",&a); while(a--) { scanf("%d%d",&b,&c); vec.clear(); for(ri i=1;i<=b;i++) { scanf("%d",&d[i]); if(d[i]<i) { while(!vec.empty()) { if(vec.back().first>=d[i]+1) { vec.pop_back(); } else { break; } } if(!vec.empty()&&vec.back().second>=d[i]+1) { vec.back().second=i; } else { vec.push_back({d[i]+1,i}); } } } scanf("%s",e+1); memset(can,0,sizeof(can)); num=0; for(register auto i:vec) { for(ri j=i.first;j<=i.second;j++) { can[j]=-1; num++; } } for(ri i=1;i<=b;i++) { if(e[i]=='L') { if(can[i]<0) { num--; } can[i]++; } else { if(can[i+1]<0) { num--; } can[i+1]++; } } while(c--) { scanf("%d",&u); if(e[u]=='L') { e[u]='R'; can[u]--; if(can[u]<0) { num++; } if(can[u+1]<0) { num--; } can[u+1]++; } else { e[u]='L'; can[u+1]--; if(can[u+1]<0) { num++; } if(can[u]<0) { num--; } can[u]++; } if(!num) { puts("YES"); } else { puts("NO"); } } } return 0; }
D. Card Game
题目内容
一种双人卡牌游戏中有
游戏开始时将牌均分。规定
思路
首先忽略掉花色
代码
#include<bits/stdc++.h> using namespace std; #define il inline #define ri register int #define inf 0x3f3f3f3f int a,b; const int mod=998244353; long long dp[505][505],ans,jc[1001],ny[1001],num[505],gt[505][505]; il long long qpow(long long x,long long y) { register long long rn=1; while(y) { if(y&1) { rn=(rn*x)%mod; } x=(x*x)%mod; y>>=1; } return rn; } il long long C(int x,int y) { return (((jc[x]*ny[x-y])%mod)*ny[y])%mod; } int main() { scanf("%d%d",&a,&b); jc[0]=ny[0]=1; for(ri i=1;i<=a+b;i++) { jc[i]=(jc[i-1]*i)%mod; ny[i]=qpow(jc[i],mod-2); } dp[0][0]=1; for(ri i=1;i<=b;i++) { for(ri j=0;j<=i;j++) { if(j) { dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod; } if(j<i) { dp[i][j]=(dp[i][j]+dp[i-1][j+1])%mod; } } } if(a==1) { printf("%lld\n",dp[b][0]); return 0; } for(ri i=0;i<=(b>>1);i++) { gt[1][i]=dp[b][i<<1]; } for(ri i=2;i<a;i++) { for(ri j=0;j<=(b>>1);j++) { for(ri k=0;k<=j;k++) { gt[i][j]+=(gt[i-1][k]*dp[b][(j-k)<<1])%mod; gt[i][j]%=mod; } } } for(ri i=0;i<=(b>>1);i++) { ans+=(dp[b][i<<1]*gt[a-1][i])%mod; ans%=mod; } printf("%lld",ans); return 0; }
E. Long Way to be Non-decreasing
题目内容
给你一个长为
-
选定一个下标集合
。 -
对于
输出使
思路
首先注意到由于集合任选,所以答案是具有显然的单调性的,于是二分答案。如果暴力
-
不在一个连通块里,必不可达。
-
在同一棵树上,终点不在环上。以环为根,但是二者不在同一颗子树内,或者在同一颗子树内但是不是祖先关系,则必不可达;否则就是二者的深度差。
-
终点在环上。距离分为从子树走到环上的距离和环上距离。前一个和前面的处理方法相同,后一个可以给环钦定一个起点,然后处理出每个点离起点的距离。
但是到了这一步好像 check 还是
代码
#include<bits/stdc++.h> using namespace std; #define il inline #define ri register int #define inf 0x3f3f3f3f int a,b,c,d[1000001],e[1000001],f[1000001],g,cnt,dfs[1000001],low[1000001],len[1000001],dep[1000001],in[1000001],out[1000001],sum[100001],be[1000001],hn[1000001],ua; stack<int>use; vector<int>vec[1000001]; bool col[1000001],ck[1000001]; struct BCJ { int fa[1000001],siz[1000001]; il void build(int x) { for(ri i=1;i<=x;i++) { fa[i]=i; siz[i]=1; } } il int find(int x) { if(fa[x]!=x) { fa[x]=find(fa[x]); } return fa[x]; } il bool merge(int x,int y) { x=find(x),y=find(y); if(x==y) { return false; } if(siz[x]<siz[y]) { swap(x,y); } fa[y]=x; siz[x]+=siz[y]; return true; } }tree; struct node { int h,to; }pic[1000001]; il void ndin(int x,int y) { g++; pic[g].to=y; pic[g].h=f[x]; f[x]=g; } void tarjan(int x) { cnt++; dfs[x]=low[x]=cnt; ck[x]=true; use.push(x); for(ri i=f[x];i;i=pic[i].h) { ri y=pic[i].to; if(y==x) { ua++; hn[x]=ua; col[x]=true; use.pop(); sum[ua]++; ck[x]=false; return; } if(!dfs[y]) { dep[y]=dep[x]+1; tarjan(y); low[x]=min(low[x],low[y]); } else if(ck[x]) { low[x]=min(low[x],dfs[y]); } } if(dfs[x]==low[x]) { if(use.top()!=x) { ua++; while(use.top()!=x) { ri i=use.top(); hn[i]=ua; col[i]=true; len[i]=dep[i]-dep[x]; ck[i]=false; use.pop(); sum[ua]++; } ck[x]=false; col[x]=true; hn[x]=ua; len[x]=0; use.pop(); sum[ua]++; } else { ck[x]=false; use.pop(); } } } void dfs0(int x,int y) { be[x]=y; cnt++; in[x]=cnt; for(ri i:vec[x]) { if(!col[i]) { dep[i]=dep[x]+1; dfs0(i,y); } } out[x]=cnt; } il int dist(int x,int y) { if(x==y) { return 0; } if(tree.find(x)!=tree.find(y)) { return inf; } if(col[y]) { if(len[y]>len[be[x]]) { return dep[x]+len[y]-len[be[x]]; } else { return dep[x]+(sum[hn[y]]-(len[be[x]]-len[y]))%sum[hn[y]]; } } if(be[x]!=be[y]||in[y]>=in[x]||out[y]<in[x]) { return inf; } return dep[x]-dep[y]; } il bool check(int x) { ri re=1; for(ri i=1;i<=b;i++) { while(re<=c&&dist(d[i],re)>x) { re++; } if(re>c) { return false; } } return true; } int main() { scanf("%d",&a); while(a--) { scanf("%d%d",&b,&c); register bool te=true; for(ri i=1;i<=b;i++) { scanf("%d",&d[i]); if(d[i]<d[i-1]) { te=false; } } fill(f+1,f+1+c,0); g=0; tree.build(c); for(ri i=1;i<=c;i++) { vec[i].clear(); } for(ri i=1;i<=c;i++) { scanf("%d",&e[i]); ndin(i,e[i]); vec[e[i]].push_back(i); tree.merge(i,e[i]); } if(te) { puts("0"); continue; } cnt=0; fill(dfs+1,dfs+1+c,0); fill(sum+1,sum+1+c,0); fill(col+1,col+1+c,false); ua=0; for(ri i=1;i<=b;i++) { if(!dfs[i]) { dep[i]=0; tarjan(i); } } for(ri i=1;i<=c;i++) { if(col[i]) { dep[i]=0; cnt=0; dfs0(i,i); } } ri m=1,n=c; while(m!=n) { ri l=(m+n)>>1; if(check(l)) { n=l; } else { m=l+1; } } if(!check(m)) { puts("-1"); } else { printf("%d\n",m); } } return 0; }
F. Many Games
题目内容
给你
求
思路
以下推式子部分默认
这个东西不好直接 dp 答案,所以把函数中的一个东西放到 dp 状态里。显然你很难把概率放到状态上,所以设 充分发扬人类智慧找找性质,或者依托强大的观察能力打表找规律,发现选中的东西不会很多。具体有多少,需要推式子。首先在最优状态下一定不会再多选或少选一个东西(废话)。然后对于概率相同的,一定选权值更大的更优。假设在当前概率
由于我们要贪心选大的,所以
于是就可以在这一步进行剪枝。只有满足上面条件的物品才可能被选中。拿个程序跑一下会发现这东西不大,378。但是我们 充分发扬人类智慧找找性质,或者依托强大的观察能力打表找规律,发现值域不会很大。设最优答案的权值和为
代码
#include<bits/stdc++.h> using namespace std; #define il inline #define ri register int #define inf 0x3f3f3f3f int a,u,v,b; vector<int>vec[101]; long double dp[220022],ans; int main() { scanf("%d",&a); for(ri i=1;i<=a;i++) { scanf("%d%d",&u,&v); vec[u].push_back(v); } vec[0].clear(); for(ri i=1;i<=99;i++) { sort(vec[i].begin(),vec[i].end()); reverse(vec[i].begin(),vec[i].end()); while(vec[i].size()>100/(100-i)) { vec[i].pop_back(); } } vec[100].push_back(0); while(vec[100].size()>1) { vec[100][0]+=vec[100].back(); vec[100].pop_back(); } dp[0]=1; for(ri i=1;i<=99;i++) { for(ri j:vec[i]) { for(ri k=200000;k>=j;k--) { dp[k]=max(dp[k],dp[k-j]*i/100); } } } for(ri i=0;i<=200000;i++) { ans=max(ans,dp[i]*(i+vec[100][0])); } printf("%.8Lf",ans); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】