Codeforces Round #857 Div.1/Div.2 CF1801/1802 2A~1F 题解
Div 2A. Likes
如果要赞最多,肯定是先放所有的点赞,再放所有移除的操作。如果要最少,那就先把赞分成两种:最后被移除的和没被移除的;最后先放所有被移除的,放一个移除一个,然后再把所有没被移除的接在最后即可。
时间复杂度。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define fi first #define se second #define pb push_back #define mpr make_pair void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nEXECUTION TERMINATED"; #endif exit(0); } using namespace std; int t,n,a[110],ans1[110],ans2[110]; int main() { fileio(); cin>>t; rep(tn,t) { cin>>n; int posi=0,nega=0; rep(i,n) cin>>a[i]; rep(i,n) if(a[i]>0) ++posi;else ++nega; int bigp=posi,bign=nega,smlp=posi,smln=nega,ansbig=0,anssml=0; repn(i,n) { if(bigp) { --bigp; ++ansbig; } else { --bign; --ansbig; } if(i<=nega*2) { if(i%2==1) ++anssml; else --anssml; } else ++anssml; ans1[i]=ansbig;ans2[i]=anssml; } repn(i,n) cout<<ans1[i]<<' ';cout<<endl; repn(i,n) cout<<ans2[i]<<' ';cout<<endl; } termin(); }
Div 2B. Settlement of Guinea Pigs
对每个时刻求出可能需要的最大猪圈数,然后取max即可。对于一个时刻,可以把猪分成两类,已经认证性别的和没认证的。没认证的只能自己住一个猪圈,因为和任意其他猪住一个都可能不合法。已经认证的,如果总数是>0的偶数,肯定是两类都是奇数个的情况最费猪圈;如果是奇数,那就是一类奇数一类偶数,猪圈数是固定的;总数是0那就是0个猪圈。
时间复杂度。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define fi first #define se second #define pb push_back #define mpr make_pair void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nEXECUTION TERMINATED"; #endif exit(0); } using namespace std; int t,n,a[100010]; int calc(int pre) { if(pre==0) return 0; if(pre==1) return 1; if(pre==2) return 2; if(pre%2==0) return (pre-2)/2+2; return (pre+1)/2; } int main() { fileio(); cin>>t; rep(tn,t) { cin>>n; int pre=0,nxt=0,ans=0; rep(i,n) { int x;scanf("%lld",&x); if(x==1) { ++nxt; ans=max(ans,nxt+calc(pre)); } else { pre+=nxt;nxt=0; ans=max(ans,calc(pre)); } } cout<<ans<<endl; } termin(); }
Div 1A/2C. The Very Beautiful Blanket
怎么每场都有猜谜题呢。。。
首先猜一个:不同数的最大个数是n*m,很多这种题都是先猜能达到最大值的
观察题目中的那个两个田字格异或和相等的限制,其实只要对的每个bit都满足就行了。对于每个bit,发现如果我们只把一些行中所有的数都刷上这个bit,其他的行不刷,是满足限制的。比如我们令第1,3,5,6行所有的数都含有,其他的行都不含有,是符合限制的,这时每个田字格的异或和都是0。发现使用这种刷一行或一列的方式,我们可以让矩阵中的所有数都不同。比如我们令数组。矩阵中第i行j列的数含有当且仅当i中含有,含有当且仅当j中含有。这么操作其实就是给一些行和一些列都刷上了某一个bit,且成功区别开了所有行和所有列,使得矩阵中每一个数都不同。
时间复杂度。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define fi first #define se second #define pb push_back #define mpr make_pair void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nEXECUTION TERMINATED"; #endif exit(0); } using namespace std; int t,n,m,ans[210][210]; vector <int> r,c; int main() { fileio(); rep(i,10) r.pb(1<<i); for(int i=10;i<20;++i) c.pb(1<<i); cin>>t; rep(tn,t) { cin>>n>>m; rep(i,n) rep(j,m) { ans[i][j]=0; rep(k,r.size()) if(i&(1<<k)) ans[i][j]|=r[k]; rep(k,c.size()) if(j&(1<<k)) ans[i][j]|=c[k]; } cout<<n*m<<endl; rep(i,n) { rep(j,m) printf("%d ",ans[i][j]); puts(""); } } termin(); }
Div 1B/2D. Buying gifts
先把所有department按从小到大排序,枚举给第一个人买的礼物最大值,此时i右侧的必须全买给第二个人,左侧的可以给第一个人也可以给第二个人。首先令i右侧的最大值为,如果,此时的答案就是;否则可以在i左侧的中找一个与最接近的再次更新答案,可以用multiset实现。需要特判一下的情况。
时间复杂度。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define pii pair <LL,LL> #define fi first #define se second #define pb push_back #define mpr make_pair void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nEXECUTION TERMINATED"; #endif exit(0); } using namespace std; LL t,n; vector <pii> a; multiset <LL> l,r; LL findNearL(LL x) { auto it=l.lower_bound(x); pii ret=mpr(1e18,1e18); if(it!=l.end()) ret=min(ret,mpr(llabs(*it-x),*it)); if(it!=l.begin()) { --it; ret=min(ret,mpr(llabs(*it-x),*it)); } return ret.se; } int main() { fileio(); cin>>t; rep(tn,t) { cin>>n; a.clear(); LL x,y; rep(i,n) { scanf("%lld%lld",&x,&y); a.pb(mpr(x,y)); } sort(a.begin(),a.end()); l.clear();r.clear(); rep(i,a.size()) r.insert(a[i].se); LL ans=1e18; rep(i,a.size()) { r.erase(r.lower_bound(a[i].se)); if(i-1>=0) l.insert(a[i-1].se); if(i==a.size()-1) break; LL mx2=*r.rbegin(); ans=min(ans,llabs(mx2-a[i].fi)); if(mx2<a[i].fi&&l.size()) { LL val=findNearL(a[i].fi); ans=min(ans,llabs(val-a[i].fi)); } } LL val=findNearL(a.back().fi); ans=min(ans,llabs(val-a.back().fi)); cout<<ans<<endl; } termin(); }
Div 1C/2E. Music Festival
首先令表示第i个专辑中的最大值,发现如果一个mx小的专辑排在一个mx大的专辑后面(mx相等也一样),那这个专辑是不能产生任何贡献的。所以可以先把所有专辑按从小到大排序,我们现在要在其中选一些使得前缀最大值的个数最多。可以用dp实现,表示处理到第i个专辑,此时被选的专辑中mx的最大值为j的最大前缀最大值个数。这状态是的,但是在处理每个i时改变的状态数都很少,所以可以用线段树维护j这一维,转移时枚举当前专辑中得到的前缀最大值个数,就能得到可以从其转移的j的范围,在线段树上询问即可。注意事项:每个测试点不能直接清空整个线段树,会TLE,要先把值域离散化后清空要用的部分。
时间复杂度。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define fi first #define se second #define pb push_back #define mpr make_pair void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nEXECUTION TERMINATED"; #endif exit(0); } using namespace std; int t,n; vector <pair <int,vector <int> > > v; vector <int> dsc; int getDSC(int x){return lower_bound(dsc.begin(),dsc.end(),x)-dsc.begin();} namespace st { int n2,dat[800010]; void upd(int k,int val) { dat[k]=max(dat[k],val); while(k>0) { k=(k-1)/2; dat[k]=max(dat[k],val); } } int qry(int k,int lb,int ub,int tlb,int tub) { if(ub<tlb||tub<lb) return 0; if(tlb<=lb&&ub<=tub) return dat[k]; return max(qry(k+k+1,lb,(lb+ub)/2,tlb,tub),qry(k+k+2,(lb+ub)/2+1,ub,tlb,tub)); } } int main() { fileio(); cin>>t; rep(tn,t) { cin>>n; v.clear();dsc.clear(); rep(i,n) { int x,y,mx=-1e9;vector <int> tmp; scanf("%d",&x); rep(j,x) { scanf("%d",&y); mx=max(mx,y);tmp.pb(y);dsc.pb(y); } v.pb(mpr(mx,tmp)); } sort(v.begin(),v.end()); sort(dsc.begin(),dsc.end());dsc.erase(unique(dsc.begin(),dsc.end()),dsc.end()); rep(i,v.size()) rep(j,v[i].se.size()) v[i].se[j]=getDSC(v[i].se[j]); st::n2=1;while(st::n2<dsc.size()) st::n2*=2; rep(i,st::n2*2+3) st::dat[i]=0; rep(i,v.size()) { vector <int> premx; int mx=-1; rep(j,v[i].se.size()) if(v[i].se[j]>mx) { mx=v[i].se[j]; premx.pb(mx); } int opt=-1; rep(i,premx.size()) { int lb=(i==0 ? 0:premx[i-1]),ub=premx[i]-1; int val=st::qry(0,0,st::n2-1,lb,ub)+premx.size()-i; opt=max(opt,val); } st::upd(premx.back()+st::n2-1,opt); } cout<<st::dat[0]<<endl; } termin(); }
Div 1D/2F. The way home
无论是cf还是atc,这种用比较复杂的状态跑最短路的题还是经常有的。
先明确我们走的思路:当要坐某趟航班钱不够时,就在我们走到过的报酬最高的城市不断补表演,直到钱够做这次航班为止。
令表示当前走到i号点,在这次走到i号点之前经过的最大的点的编号为j,此时的最小表演数和最大剩余钱数(先按最小表演数从小到大排序,相同的再按剩余钱数从大到小排序)。我们来证明:如果有两个可能的状态和(ac表示最小表演数,bd表示钱数),且a<c,b<d,此时第一个状态一定优于第二个。用一种类似归纳的思想,我们假设已经对这两个状态转移过来的路径上的所有都证明了其最优状态唯一。由于这两个状态在这次到i之前经过的报酬最高的点都是j,因此它们是从同一个转移过来的,在那之后它们的表演数才变得不同。因此第二个状态的d肯定,因为我们补表演是补到能坐航班就停了,所以剩下的一定。因此如果给第一个状态补次表演,使两个状态表演次数相同,第一个的钱数一定比第二个多,所以第一个一定更优。
然后就用上面的状态跑一个Dijkstra就行了,时间复杂度(可能?没仔细算)。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define pii pair <LL,LL> #define fi first #define se second #define pb push_back #define mpr make_pair void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nEXECUTION TERMINATED"; #endif exit(0); } using namespace std; LL t,n,m,p,w[810]; vector <pii> g[810]; pii dist[810][810]; priority_queue <pair <pii,pii> > q; int main() { fileio(); cin>>t; rep(tn,t) { cin>>n>>m>>p; repn(i,n) scanf("%lld",&w[i]); rep(i,n+3) g[i].clear(); LL x,y,z; rep(i,m) { scanf("%lld%lld%lld",&x,&y,&z); g[x].pb(mpr(y,z)); } rep(i,n+3) rep(j,n+3) dist[i][j]=mpr(-1e18,-1e18); dist[1][0]=mpr(0,p); q.push(mpr(mpr(0,p),mpr(1,0))); while(!q.empty()) { auto fr=q.top();q.pop(); pii val=fr.fi;LL pos=fr.se.fi,opt=fr.se.se; if(dist[pos][opt]>val) continue; if(w[pos]>w[opt]) opt=pos; rep(i,g[pos].size()) { pii nval=val; if(nval.se<g[pos][i].se) { LL add=(g[pos][i].se-nval.se+w[opt]-1)/w[opt]; nval.fi-=add;nval.se+=add*w[opt]; } nval.se-=g[pos][i].se; if(dist[g[pos][i].fi][opt]<nval) { dist[g[pos][i].fi][opt]=nval; q.push(mpr(nval,mpr(g[pos][i].fi,opt))); } } } pii ans=mpr(-1e18,-1e18); repn(i,n) ans=max(ans,dist[n][i]); if(ans.fi< -1e17) puts("-1"); else cout<< -ans.fi<<endl; } termin(); }
由于我用小号打的Div2,所以看不到过了一车的那个1F,1E也不会。后面如果有时间会更新1E和1F的题解
UPD: Div.1 E F题解
link
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话