2024.12.15 THUPC2025初赛
Solved: 6/13
Rank: 295
300 多人过的网络流题没思路,直接从 200- 掉到 300-……
M. 好成绩
致敬主=6()
print(83)
C. Harmful Machine Learning
题意:
先手希望结束的时候棋子所在的格子中的数最小,后手希望最大,求最优决策下的答案。
一步之后,先手就可以将最小的三个数都移到后手的格子以及相邻两个格子的位置。因此后手的唯一机会就在第一步。
先手针对后手的第一步可以将最小值移到到后手下一步能到的三个格子中,因此后手第一步只能取到这三个格子中第二大的值。
答案就是
特判
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define all(x) (x).begin(),(x).end() void solve(){ int n,x; cin>>n>>x,--x; vector<int> a(n); for(int i=0;i<n;++i)cin>>a[i]; vector<int> b(a); sort(all(b)); if(n<=3){ cout<<b[n-1]<<'\n'; } else{ if(x==0){ cout<<max(min(a[0],a[1]),b[2])<<'\n'; } else if(x==n-1){ cout<<max(min(a[n-1],a[n-2]),b[2])<<'\n'; } else{ vector<int> c({a[x-1],a[x],a[x+1]}); sort(all(c)); cout<<max(c[1],b[2])<<'\n'; } } } int main(){ ios::sync_with_stdio(0);cin.tie(0); int T; cin>>T; while(T--)solve(); }
I. 乒乓球赛
题意:两人打乒乓球,10平前先得11分者胜,10平后先超对方2分者胜。已知比赛过程中的一些比分情况(顺序可能颠倒,即 3:5 念成 5:3),求符合已知条件的方案数。
dp,
如果两个状态都没到 10 平,直接组合数即可(
如果两个状态都过了 10 平,则
如果前面没过后面过了,则一定存在某一时刻比分是 10 平,两段分开乘即可。
注意特判各种无解不合法情况。
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define all(x) (x).begin(),(x).end() const int N=1e5+5,mod=998244353; int c[21][21]; ll h[N][3][3]; void init(){ for(int i=0;i<=20;++i){ c[i][0]=1; for(int j=1;j<=i;++j) c[i][j]=c[i-1][j]+c[i-1][j-1]; } h[0][0][0]=h[0][1][1]=h[0][2][2]=1; int m=1e5; for(int i=0;i<=m;++i) for(int j=0;j<3;++j){ (h[i+1][j][0]+=h[i][j][1])%=mod; (h[i+1][j][1]+=h[i][j][0])%=mod; (h[i+1][j][1]+=h[i][j][2])%=mod; (h[i+1][j][2]+=h[i][j][1])%=mod; } } ll F(int x1,int y1,int x2,int y2){ if(x2<x1||y2<y1)return 0; if(x2+y2<=20){ x2=min(x2,10),y2=min(y2,10); return c[x2+y2-x1-y1][x2-x1]; } if(x1+y1>=20){ int s=x2+y2-x1-y1,d1=x1-y1,d2=x2-y2; if(d2==2)--s,--d2; if(d2==-2)--s,++d2; return h[s][d1+1][d2+1]; } return F(x1,y1,10,10)*F(10,10,x2,y2)%mod; } ll solve(){ int n; cin>>n; vector<int> a(n+1),b(n+1); vector<ll> f(n+1),g(n+1); for(int i=1;i<=n;++i){ cin>>a[i]>>b[i]; if(a[i]<b[i])swap(a[i],b[i]); } if(n<=20){ if(a[n]!=-1&&(a[n]!=11||b[n]!=n-11))return 0; a[n]=11,b[n]=n-11; } else{ if(n%2==1)return 0; if(a[n]!=-1&&(a[n]!=n/2+1||b[n]!=n/2-1))return 0; a[n]=n/2+1,b[n]=n/2-1; } auto chk=[=](int x,int y)-> bool { if(x+y<=20)return x>10; return x-y>1; }; f[0]=1,g[0]=0; for(int i=1,j=0;i<=n;++i){ if(a[i]==-1)continue; if(a[i]+b[i]!=i)return 0; if(chk(a[i],b[i])&&i<n)return 0; f[i]=(f[j]*F(a[j],b[j],a[i],b[i])+g[j]*F(b[j],a[j],a[i],b[i]))%mod; if(a[i]!=b[i])g[i]=(f[j]*F(a[j],b[j],b[i],a[i])+g[j]*F(b[j],a[j],b[i],a[i]))%mod; j=i; } return (f[n]+g[n])%mod; } int main(){ init(); ios::sync_with_stdio(0);cin.tie(0); int T; cin>>T; while(T--)cout<<solve()<<'\n'; }
J. 辞甲猾扎
题意:树上有一些黑点,你可以选一些未染色的点染白。白点和黑点会一起扩散,每次扩散一条边。
一个点如果仅被白点或黑点扩散则它会变成对应颜色,如果同时被白点和黑点扩散则会变成白色。
求使黑点完全无法扩散最少要染多少个白点。
本质上要求染色的点“占领”了所有与黑点相邻的点(但不需要占领黑点)。
这里“占领”指重合或相邻。
这是经典的“点覆盖点”问题。
树形dp,
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define all(x) (x).begin(),(x).end() const int N=1e6+5,inf=1e9; int n,x,y,f[N],g[N],h[N]; bool a[N],b[N]; vector<int> e[N]; inline void adde(int x,int y){e[x].push_back(y);} void dfs(int u,int fa){ f[u]=1; if(b[u])f[u]=inf; if(b[fa])h[u]=inf; int tmp=inf; for(int v:e[u])if(v^fa){ dfs(v,u); int t=min(f[v],g[v]),tt=min(t,h[v]); f[u]+=tt; g[u]+=t,h[u]+=t; tmp=min(tmp,f[v]-g[v]); } if(tmp>0&&a[u])g[u]+=tmp; } int k; vector<int> p; int main(){ ios::sync_with_stdio(0);cin.tie(0); cin>>n>>k; p.resize(k); for(int& x:p)cin>>x,b[x]=1; for(int i=1;i<n;++i)cin>>x>>y,adde(x,y),adde(y,x); for(int x:p)for(int y:e[x])if(!b[y])a[y]=1; dfs(1,0); cout<<min(f[1],g[1])<<'\n'; }
D. 摊位分配
题意:有
- 优先取
大的; - 如相等,优先取未分配的;
- 如还相等,优先取初始
大的; - 如还相等,优先取
小的。
然后把摊位分配给相应的社团。问最终每个社团能分到多少摊位。
题目中的方案等价于,一个社团分到一个摊位之后就把它的贡献除 2 再去和剩下的比。
注意到至多
可以用优先队列维护这个过程。
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define all(x) (x).begin(),(x).end() const int N=1e5+5; int n,m,q,ans[N]; struct node{ double u; int id,ans; bool operator < (const node& p)const{ if(u!=p.u)return u<p.u; if(!ans)return 0; if(!p.ans)return 1; return id>p.id; } }a[N]; int id[N]; bool cmp(node a,node b){ return a.u>b.u||a.u==b.u&&a.id<b.id; } int main(){ ios::sync_with_stdio(0);cin.tie(0); cin>>n>>m; if(m>n*30)q=(m-n*30)/n,m-=q*n; for(int i=1;i<=n;++i)cin>>a[i].u,a[i].id=i; sort(a+1,a+n+1,cmp); for(int i=1;i<=n;++i)id[a[i].id]=i,a[i].id=i; priority_queue<node> pq; for(int i=1;i<=n;++i)pq.push(a[i]); for(int i=1;i<=m;++i){ node u=pq.top(); pq.pop(); ++u.ans,++ans[u.id],u.u/=2; pq.push(u); } for(int i=1;i<=n;++i)cout<<ans[id[i]]+q<<' '; cout<<'\n'; }
G. Imyourfan
题意:初始有一个仅含 WMX
三个字符的字符串。WM 两人轮流操作,每个人可以找一个不含 X
的子区间删去。若某次操作后
- 所有
W
都被删去:W 胜; - 所有 'M' 都被删去:M 胜;
- 所有
WM
都被删去:平局。
求最优决策下的结果。
把原串按 X
分隔开,每一段是独立的。
对每个不含 X
的段,单独考虑:
- 如果
M
的段数比W
多(等价于首尾都是M
),则 W 必胜; - 如果
W
的段数比M
多(等价于首尾都是W
),则 M 必胜; - 如果段数一样多,则谁先开始取谁必胜。
因为 W 先取,所以当 W 必胜段数大于等于 M 必胜段数时,W 占优;反之 M 占优。
当 W 必胜段数等于 M 必胜段数时,有一种特殊情况可以使 M 达到平局:就是取到最后只剩一段,这段是 W 必胜且不是纯 M 段。此时 M 可以一次性全部取完。
M 必胜段数等于 W 必胜段数 +1 时同理。
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define all(x) (x).begin(),(x).end() void solve(){ string s; cin>>s; int n=s.length(); int cnt1=0,cnt2=0,cnt3=0,cnt4=0,cnt0=0; for(int i=0,o=0,l=0;i<=n;++i){ if(i==n||s[i]=='X'){ if(o>0){ if(l==1)++cnt1; else ++cnt3; } else if(o<0){ if(l==1)++cnt2; else ++cnt4; } else ++cnt0; o=l=0; } else if(s[i]=='W'){ if(i==0||s[i-1]!='W')++o,++l; } else if(s[i]=='M'){ if(i==0||s[i-1]!='M')--o,++l; } } int d=cnt1+cnt3-cnt2-cnt4; if(d<0)cout<<"Water\n"; else if(d>1)cout<<"Menji\n"; else if(d==0){ if(cnt4>0)cout<<"Draw\n"; else cout<<"Water\n"; } else if(d==1){ if(cnt3>0)cout<<"Draw\n"; else cout<<"Menji\n"; } } int main(){ ios::sync_with_stdio(0);cin.tie(0); int T; cin>>T; while(T--)solve(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)