CSP2025 题解

news/2025/11/4 14:11:42/文章来源:https://www.cnblogs.com/FloatingLife/p/19189579

你猜我为什么不写游记。

社团招新(club)

直接贪心即可。

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
using namespace std;
const int N=1e5+5;inline int read(){int w=1,s=0;char c=getchar();for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());return w*s;
}
int T,n,a[N][5],id[N],cnt[5],ans; 
signed main(){
//	freopen("club.in","r",stdin);
//	freopen("club.out","w",stdout);T=read();while(T--){cnt[1]=cnt[2]=cnt[3]=0;n=read();ans=0;for(int i=1;i<=n;i++){int maxn=0;for(int j=1;j<=3;j++) a[i][j]=read(),maxn=max(maxn,a[i][j]);for(int j=1;j<=3;j++){if(maxn==a[i][j]){id[i]=j,cnt[j]++;break;}}ans+=maxn;}int mx=max({cnt[1],cnt[2],cnt[3]});if(mx>n/2){int o;for(int i=1;i<=3;i++) if(mx==cnt[i]) o=i;vector<int> vec;for(int i=1;i<=n;i++){if(id[i]==o){int se=0;for(int j=1;j<=3;j++) if(j!=o) se=max(se,a[i][j]);vec.push_back(a[i][o]-se);}}sort(vec.begin(),vec.end());for(int i=0;i<mx-n/2;i++) ans-=vec[i];}printf("%d\n",ans);}return 0;
}

道路修复(road)

根据 Kruskal 的过程不难发现在加上那 \(O(nk)\) 条边后原图的边只有原最小生成树上的边是有用的,因此有用的边数是 \(O(nk)\) 的。
然后 \(O(2^k)\) 枚举选择了哪些新点,然后跑 Kruskal 求出此时的最小生成树即可。
可以提前在外面排好 \(O(nk)\) 条边的顺序,这样枚举之后就不需要再排一遍序了。
\(O(2^knk\alpha(n))\),应该不是正解,但跑得挺快的。

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
#define LL long long 
using namespace std;
const int N=1e4+100,M=1e6+5;inline int read(){int w=1,s=0;char c=getchar();for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());return w*s;
}
int n,m,k,fa[N],c[15],a[15][N],tot;
bool flag[15];
LL ans=LLONG_MAX;
struct Edge{ int u,v,w,op; } E[M],MST[N];
int get(int x){ return (x==fa[x])?(x):(fa[x]=get(fa[x])); }
bool cmp(Edge x,Edge y){ return x.w<y.w; }
void Kruskal(){for(int i=1;i<=n;i++) fa[i]=i;sort(E+1,E+m+1,cmp);for(int i=1;i<=m;i++){int x=E[i].u,y=E[i].v;if(get(x)==get(y)) continue;MST[++tot]=E[i],fa[get(x)]=get(y);}
}
void Init(){Kruskal();for(int i=1;i<=tot;i++) E[i]=MST[i];for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) E[++tot]={n+i,j,a[i][j],i};sort(E+1,E+tot+1,cmp);
}
LL work(int S){LL res=0;for(int i=1;i<=k;i++) (S>>(i-1)&1)?(res+=c[i],flag[i]=true):(flag[i]=false);for(int i=1;i<=n+k;i++) fa[i]=i;for(int i=1;i<=tot;i++){if(!flag[E[i].op]) continue;int x=E[i].u,y=E[i].v,w=E[i].w;if(get(x)==get(y)) continue;fa[get(x)]=get(y),res+=w;}return res;
}
signed main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);double beg=clock();n=read(),m=read(),k=read();for(int i=1;i<=m;i++){int u=read(),v=read(),w=read();E[i]={u,v,w,0};}for(int i=1;i<=k;i++){c[i]=read();for(int j=1;j<=n;j++) a[i][j]=read();}Init();flag[0]=true;for(int S=0;S<(1<<k);S++) ans=min(ans,work(S));printf("%lld\n",ans);cerr << fixed << setprecision(3) << (double)(clock()-beg) << endl;return 0;
}

谐音替换(replace)

先求出询问串 \(t_1,t_2\) 的 LCP 和 LCS,不妨设 \(t_1=ACB,t_2=ADB\),那显然如果一对 \((s_1,s_2)\) 合法需要满足他俩中间不一样的部分也分别为 \(C,D\),不妨设 \(s_1=A'CB',s_2=A'DB'\),那么合法的另一个条件是 \(B'\)\(B\) 的前缀,\(reverse(A')\)\(reverse(A)\) 的前缀。
因此先用哈希(比如 mapvector)将所有 \(C,D\) 相同的二元组和询问分到同一类里,对每一类单独求解。在每一类中,对每个 \((s_1,s_2)\)\((t_1,t_2)\)\(A\) 倒序插入第一棵 Trie,将 \(B\) 正序插入第二棵 Trie,那么合法的条件可以转化为 \((t_1,t_2)\) 在两棵 Trie 上对应的节点均在 \((s_1,s_2)\) 对应的节点子树内,这是经典二维偏序,直接树状数组即可。
复杂度 \(O((L1+L2)|\sum| + (n+q)\log L)\)
Tip:题目没保证 \(|t_1|=|t_2|\),注意特判。

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
#define ULL unsigned long long 
#define PII pair<int,int>
#define fi first
#define se second 
#define mk make_pair
using namespace std;
const int N=2e5+5,M=5e6+5,p=13331;inline int read(){int w=1,s=0;char c=getchar();for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());return w*s;
}
ULL mi[M];
int n,q,ans[N],Ls[N],Rs[N],len1[N],Lt[N],Rt[N],len2[N];
string s[N][2],t[N][2];
ULL Hash(string &s,int l,int r){ULL H=0;for(int i=l;i<=r;i++) H=H*p+s[i];return H;
}
map<ULL,vector<PII> > mp;
void init(string &s,string &t,int &len,int &L,int &R,int op,int id){len=s.size();s=' '+s,t=' '+t;L=0,R=len+1;for(int j=1;j<=len;j++){if(s[j]==t[j]) L=j;else break;}for(int j=len;j>=1;j--){if(s[j]==t[j]) R=j;else break;}		if(L==len) return;int l=R-L+1;mp[Hash(s,L+1,R-1)*mi[l]+Hash(t,L+1,R-1)].push_back({op,id});
} 
namespace Solve{struct Trie{int ch[M][26],tot,dfn[M],siz[M],num;void Init(){for(int i=1;i<=tot;i++) memset(ch[i],0,sizeof ch[i]);tot=1,num=0;}int insert(string &s,int l,int r,int o){int p=1;for(int i=(o?r:l);o?(i>=l):(i<=r);o?(i--):(i++)){int c=s[i]-'a';if(!ch[p][c]) ch[p][c]=++tot;p=ch[p][c];}return p;}void dfs(int u){dfn[u]=++num,siz[u]=1;for(int i=0;i<=25;i++) if(ch[u][i]) dfs(ch[u][i]),siz[u]+=siz[ch[u][i]];}}T1,T2;PII ed1[N],ed2[N];struct BIT{int c[M];void init(int num){ for(int i=1;i<=num;i++)	c[i]=0; }int lowbit(int x){ return x&(-x); }void add(int i,int x,int num){	for(;i<=num;i+=lowbit(i)) c[i]+=x; }int ask(int i,int sum=0){for(;i;i-=lowbit(i)) sum+=c[i];return sum;}}Bit;struct P{ int op,x,y1,y2; }a[N*3];bool cmp(P x,P y){if(x.x!=y.x) return x.x<y.x;return x.op<y.op;}void work(vector<PII> vec){T1.Init(),T2.Init();for(PII o:vec){int op=o.fi,id=o.se;if(op==0) ed1[id]=mk(T1.insert(s[id][0],1,Ls[id],1),T2.insert(s[id][1],Rs[id],len1[id],0));else ed2[id]=mk(T1.insert(t[id][0],1,Lt[id],1),T2.insert(t[id][1],Rt[id],len2[id],0)); }T1.dfs(1),T2.dfs(1);int cnt=0;for(PII o:vec){int op=o.fi,id=o.se,u,v;if(op==0){u=ed1[id].fi,v=ed1[id].se;a[++cnt]={0,T1.dfn[u],T2.dfn[v],T2.dfn[v]+T2.siz[v]};a[++cnt]={-1,T1.dfn[u]+T1.siz[u],T2.dfn[v],T2.dfn[v]+T2.siz[v]};}else{u=ed2[id].fi,v=ed2[id].se;a[++cnt]={id,T1.dfn[u],T2.dfn[v],0};}}sort(a+1,a+cnt+1,cmp);int num=T2.num;Bit.init(num);for(int i=1;i<=cnt;i++){int op=a[i].op;if(op==0) Bit.add(a[i].y1,1,num),Bit.add(a[i].y2,-1,num);else if(op==-1) Bit.add(a[i].y1,-1,num),Bit.add(a[i].y2,1,num);else ans[op]=Bit.ask(a[i].y1);}}
}
signed main(){
//	freopen("replace.in","r",stdin);
//	freopen("replace.out","w",stdout);double beg=clock();ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);mi[0]=1;for(int i=1;i<M;i++) mi[i]=mi[i-1]*p;cin>>n>>q;for(int i=1;i<=n;i++){cin>>s[i][0]>>s[i][1];init(s[i][0],s[i][1],len1[i],Ls[i],Rs[i],0,i);}for(int i=1;i<=q;i++){cin>>t[i][0]>>t[i][1];if(t[i][0].size()!=t[i][1].size()) continue;init(t[i][0],t[i][1],len2[i],Lt[i],Rt[i],1,i);		}for(pair<ULL,vector<PII> > o:mp) Solve::work(o.se);for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);cerr << fixed << setprecision(3) << (double)(clock()-beg) << endl;return 0;
}

员工招聘(employ)

\(cnt_i\) 表示耐心值为 \(i\) 的人数,\(pre\)\(cnt\) 的前缀和。
一个显然的 DP 状态是 \(f_{i,j}\) 表示前 \(i\) 个人,有 \(j\) 个人被拒绝了的方案数。但发现这样是转移不动的,因为比如当我们需要往第 \(i+1\) 放一个 \(c\le j\) 的人时,由于我们并不知道前 \(i\) 个人具体的耐心值情况,所以并不知道还可以放的人有多少。

因此我们不妨加一维 \(f_{i,j,k}\) 其中 \(k\) 表示前 \(i\) 个人中有 \(k\) 个人的耐心值 \(\le j\)。此时我们就知道,如果我要在 \(i+1\) 放一个耐心值 \(\le j\) 的人,那么总共就有 \(pre_j-k\) 的方案了。但是一个新的问题是,当第 \(i+1\) 个人被拒绝时,\(j\) 会变成 \(j+1\),此时我们就需要知道 \(c\le j+1\) 的人数了,但显然根据我们目前的状态,这个信息是不能直接推出来的。

解决方法其实也比较套路,我们考虑贡献延后计算,先不去确定那 \(i-k\)\(c>j\) 的人的具体的耐心值,而是在 \(j\to j+1\) 时,再去分配有哪些人的 \(c=j+1\),具体的,考虑刷表法:

  1. \(s_{i+1}=1\) 且第 \(i+1\) 个人被录取,选一个 \(c>j\) 的人即可,但先不去确定他是谁:\(f_{i,j,k} \to f_{i+1,j,k}\)
  2. \(s_{i+1}=1\) 且第 \(i+1\) 个人不被录取,先从 \(pre_j-k\) 个人当中选一个 \(c\le j\) 的人,再枚举 \(l\) 表示那 \(i-k\) 个人当中有几个人的耐心值为 \(j+1\)\(f_{i,j,k}\times (pre_j-k) \times \binom{i-k}{l} \times \binom{cnt_{j+1}}{l} \times l! \to f_{i+1,j+1,k+l+1}\)
  3. \(s_{i+1}=0\) 此时无论填什么都会拒绝录取,因此转移跟 2 差不多,具体看代码。

看似状态数是 \(O(n^3)\),转移枚举 \(l\)\(O(n)\) 的,但是注意到 \(l\le cnt_{j+1}\),因此当 \(i,k\) 确定时,对于所有 \(1\le j\le n\)\(l\) 的总枚举量为 \(O(n)\)。总复杂度为 \(O(n^3)\)

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
using namespace std;
const int N=5e2+5,mod=998244353;inline int read(){int w=1,s=0;char c=getchar();for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());return w*s;
}
int n,m,cnt[N],pre[N],fact[N],inv[N],f[N][N][N];
char s[N]; 
int C(int n,int m){ if(n<0||m<0||n<m) return 0;return 1ll*fact[n]*inv[m]%mod*inv[n-m]%mod; 
}
void Add(int &x,int y){ (x+=y)%=mod; }
signed main(){
//	freopen("employ.in","r",stdin);
//	freopen("employ.out","w",stdout);double beg=clock();fact[0]=inv[0]=inv[1]=1;for(int i=1;i<N;i++) fact[i]=1ll*fact[i-1]*i%mod;for(int i=2;i<N;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;for(int i=2;i<N;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;scanf("%d%d%s",&n,&m,s+1);for(int i=1,x;i<=n;i++) scanf("%d",&x),cnt[x]++;pre[0]=cnt[0];for(int i=1;i<=n;i++) pre[i]=cnt[i]+pre[i-1];f[0][0][0]=1;for(int i=0;i<n;i++){for(int j=0;j<=i;j++){for(int k=0;k<=min(i,pre[j]);k++){if(!f[i][j][k]) continue;int val=f[i][j][k];if(s[i+1]=='1'){Add(f[i+1][j][k],val);  //通过,选一个耐心值 >j 的人 if(k<pre[j]){for(int l=0;l<=min(cnt[j+1],i-k);l++){  //不通过,选一个耐心值 <=j 的人,并决策之前哪些人的耐心值 =j+1 Add(f[i+1][j+1][k+l+1],1ll*val*(pre[j]-k)%mod*C(i-k,l)%mod*C(cnt[j+1],l)%mod*fact[l]%mod);}}} else{ //必定失败 for(int l=0;l<=min(cnt[j+1],i-k);l++){Add(f[i+1][j+1][k+l],1ll*val*C(i-k,l)%mod*C(cnt[j+1],l)%mod*fact[l]%mod);  //耐心值 >j+1if(k+l<pre[j+1]) Add(f[i+1][j+1][k+l+1],1ll*val*(pre[j+1]-k-l)%mod*C(i-k,l)%mod*C(cnt[j+1],l)%mod*fact[l]%mod);  //耐心值 <=j+1 }					}}}}int ans=0;for(int j=0;j<=n-m;j++) Add(ans,1ll*f[n][j][pre[j]]*fact[n-pre[j]]%mod);printf("%d\n",ans);cerr << fixed << setprecision(3) << (double)(clock()-beg) << endl;return 0;
}

后记

考场上玩了一整场原神,什么都没写出来。不知道场上咋想的,认为 \(dfn_x\le dfn_y\le dfn_x+siz_x-1\) 是二维偏序,结果就变成四维偏序了,然后都写一半了认为自己做法是错的把代码全删了。成功被机房所有同学偏序。
希望是给 NOIP 攒 RP 吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/955659.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

都昌电子病历编辑器最新特性

都昌电子病历编辑器最新特性都昌电子病历编辑器(DCwriter)是我司的核心产品。2023 年发布的基于 WASM+Canvas 的 DCWriter 5.0 已成功上线数千家医院,但其依赖资源较大的问题受到用户反馈。为解决这一问题,我们深入…

C/C++跳动的爱心③ - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年五大靠谱劳动仲裁律师服务公司推荐

在劳动纠纷频发的当下,专业的劳动仲裁律师服务显得尤为重要。当劳动者或企业面临劳动仲裁相关问题时,选择一家靠谱的律师服务公司至关重要。以下为您推荐2024年五家靠谱的提供劳动仲裁律师服务的公司。 一、北京市炜…

2025年电梯装潢开槽机厂商权威推荐榜单:铝单板开槽机/幕墙开槽机/门业开槽机源头厂家精选

作为电梯装潢生产线上的核心设备,开槽机的精度与可靠性直接影响着电梯内饰板的加工质量与生产效率。据2024年行业数据显示,中国电梯装潢开槽设备市场规模已突破12亿元,其中数控开槽机占比达67.5%,年增长率稳定在8.…

【MySQL 从入门到精通:核心知识点与实战优化指南】 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年东北地区电气自动化公司排名,东宇电气市场口碑与产品质量全解析

在电气自动化领域,一家公司的市场口碑、品牌知名度以及产品质量稳定性是衡量其综合实力的关键指标。以下为您介绍东北地区五家靠谱的电气自动化公司,其中辽宁东宇电气自动化技术有限公司表现尤为突出。 一、辽宁东宇…

2025年四通球阀生产厂家权威推荐榜单:不锈钢法兰球阀/低温球阀/不锈钢三通球阀源头厂家精选

四通球阀作为工业管道系统中的关键控制元件,凭借其灵活的流道设计和可靠的密封性能,广泛应用于石油化工、电力、冶金等领域。据行业数据统计,2024年全球四通球阀市场规模预计突破42亿美元,其中中国市场占比达35%,…

2025 年启闭机制造厂家最新推荐:权威测评排名榜单及品牌选择全方位指南固定卷扬启闭机/手电两用螺杆启闭机/电装启闭机公司推荐

引言 在水利工程领域,启闭机作为控制闸门运行的核心设备,其质量与性能直接关乎工程安全与使用寿命。当前市场上启闭机制造厂家数量众多,产品质量差异显著,给采购方选型带来极大困扰。为助力行业从业者精准筛选优质…

2025年财产分割律师事务所权威推荐,财产分割律师哪家好

在当今社会,涉及财产分割的法律事务日益复杂,无论是婚姻中的财产分配,还是家族内部的资产划分,都需要专业、权威的律师团队介入。财产分割律师哪家权威?财产分割实力律师又该如何选择?这成为众多当事人亟待解决的…

2025年电缆厂家权威推荐榜单:铜线/三芯/铝芯/感温/矿物质/平方铜/平方铝电缆源头厂家精选

面对庞大的电缆市场需求,这些领军企业正以技术创新与严谨品质赢得市场信赖。 电线电缆作为国民经济建设的重要配套产业,其市场规模持续扩大。最新研究报告显示,全球低压电缆市场预计将从2022年的1589亿美元增长至20…

2025年11月空气能厂家推荐榜:五家优质企业横向对比与深度解析

在当前的能源转型和环保政策推动下,空气能热泵技术凭借其高效节能的特性,正成为供暖、热水供应等领域的重要选择。许多家庭用户、商业项目负责人或工程承包商在选择空气能厂家时,常常面临技术可靠性、产品适应性、售…

2025年11月止痒控油洗发水推荐对比:权威评测与用户口碑排行

随着生活节奏加快和工作压力增大,越来越多的人开始面临头皮健康问题。2025年的市场数据显示,我国脱发人群已超过2.5亿,其中脂溢性脱发占比达到70%以上。这类人群普遍存在头皮出油、瘙痒、头皮屑增多等困扰,迫切需要…

2025年靠谱离心机生产厂家推荐,专业离心机设计与制作企业全解析

在科研、医疗、化工等众多领域,离心机作为关键设备,其性能与质量直接影响着生产与研究的效率和成果。面对市场上众多离心机生产厂家,如何抉择?以下依据不同类型,为你推荐2024年十大靠谱离心机生产厂家。 一、专业…

2025年空气能和太阳能品牌及维修服务公司排名推荐

在当今注重环保与节能的时代,空气能和太阳能设备凭借其高效、清洁的特性,在众多领域得到广泛应用。无论是工厂车间、娱乐场所,还是医院、学校等,都能看到它们的身影。然而,面对市场上众多的空气能和太阳能品牌以及…

2025 国内外运维监控厂商选型指南:锚定国产化与信创,国产监控产品如何破局?

文章对比国内外多款运维监控系统,分析其在全栈信创适配、可观测能力等方面的差异,按信创需求优先级、IT架构复杂度给出选型建议,助力企业挑选契合自身场景的IT监控方案。01. 国内外运维监控厂商核心能力对比 1)嘉为…

[python刷题记录]-盛最多水的容器-双指针-中等

[python刷题记录]-盛最多水的容器-双指针-中等链接:11. 盛最多水的容器 - 力扣(LeetCode) 双指针从头尾开始,每次移动height值更小的一个,头尾向中间移动1 class Solution(object):2 def maxArea(self, heigh…

2025年黑胶唱机制造企业权威推荐榜单:黑胶唱机选购/顶级黑胶唱机/黑胶唱机推荐源头厂家精选

行业趋势:黑胶复兴带动制造升级 根据国际音乐唱片业协会数据,2024年全球黑胶唱片收入达到49.3亿美元,同比增长17.3%,连续15年保持增长。这一趋势直接带动黑胶唱机市场需求,预计2025年全球黑胶唱机市场规模将突破1…

2025年11月空气能厂家推荐榜:五大品牌综合对比与选购指南

随着冬季采暖需求日益增长,越来越多的家庭和企业开始关注空气能产品。在选择空气能厂家时,用户通常面临技术可靠性、售后服务、产品适应性等多重考量。尤其是北方严寒地区用户需要确保设备在低温环境下稳定运行,而南…

证券公司和期货公司采购 Veighna 软件相关版权风险合规报告

所有证券公司和期货公司:近期市场反馈 Veighna 开源项目存在版权归属争议,结合《计算机软件保护条例》及相关法律实践,该争议可能对贵司采购使用相关软件的合规性造成影响。为协助贵司规避潜在法律与业务风险,特此…

7-3 集合基础

集合基础 概述: 集合是一种容器,用来装数据的,类似于数组 与数组的区别与使用场景: 数组定义完成并启动后,长度就固定了 集合大小可变,开发中用的更多 使用场景: 数组:存储的元素个数固定不变 集合:存储的元素…