【比赛记录】2025CSP-S模拟赛7

A. Lesson5!

首先预处理出以每个点为起点和终点的最长路 \(g_u\)\(f_u\)。然后按照拓扑序遍历每个点,删掉与这个点相关的边后更新答案,再加上相关的边(要将所有从这个点连出的边都加上,方便后面删边)。需要可删堆。注意 \(n=1\) 的情况,维护的边集中将没有元素,所以要特判掉。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,maxm=5e5+5;
int T,n,m,f[maxn],g[maxn];
int d1[maxn],d2[maxn],deg[maxn];
vector<int> e1[maxn],e2[maxn];
struct{
	int u,v;
}E[maxm];
queue<int> q;
il void solve(){
	cin>>n>>m;
	if(n==1){
		cout<<"1 0\n";
		return ;
	}
	for(int i=1;i<=m;i++){
		cin>>E[i].u>>E[i].v;
	}
	for(int i=1;i<=n;i++){
		e1[i].clear();
		e2[i].clear();
	}
	for(int i=1,u,v;i<=m;i++){
		u=E[i].u,v=E[i].v;
		e1[u].pb(v),e2[v].pb(u);
		d1[v]++,d2[u]++,deg[v]++;
	}
	memset(f,0,sizeof f);
	memset(g,0,sizeof g);
	for(int i=1;i<=n;i++){
		if(!d1[i]){
			q.push(i);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int v:e1[u]){
			f[v]=max(f[v],f[u]+1);
			if(--d1[v]==0){
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(!d2[i]){
			q.push(i);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int v:e2[u]){
			g[v]=max(g[v],g[u]+1);
			if(--d2[v]==0){
				q.push(v);
			}
		}
	}
	priority_queue<int> q1,q2;
	for(int i=1;i<=n;i++){
		q1.push(g[i]);
	}
	for(int i=1;i<=n;i++){
		if(!deg[i]){
			q.push(i);
		}
	}
	int ans=1e9,res=0;
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int v:e1[u]){
			if(--deg[v]==0){
				q.push(v);
			}
		}
		q2.push(g[u]);
		for(int v:e2[u]){
			q2.push(f[v]+1+g[u]);
		}
		while(q2.size()&&q1.top()==q2.top()){
			q1.pop(),q2.pop();
		}
		int tmp=q1.top();
		if(ans>tmp||ans==tmp&&res>u){
			ans=tmp,res=u;
		}
		q1.push(f[u]);
		for(int v:e1[u]){
			q1.push(f[u]+1+g[v]);
		}
	}
	cout<<res<<" "<<ans<<"\n";
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		solve();
	}
	return 0;
}
}
int main(){return asbt::main();}

B. 贝尔数

那个模数可以拆成 \(31\times37\times41\times43\times47\)。那么对这几个质数分别求答案再 \(crt\) 即可。

对于一个质数 \(p\),根据第一个公式可以把 \(0\)\(p\) 的贝尔数都求出来。然后根据第二个公式再矩阵转移一下即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int mod[]={31,37,41,43,47};
int T,n,fac[55],C[55][55],ans[5];
struct juz{
	int n,mat[55][55];
	juz(int n):n(n){
		memset(mat,0,sizeof mat);
	}
	il int*operator[](int x){
		return mat[x];
	}
	il juz operator*(juz x)const{
		juz res(n);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				for(int k=1;k<=n;k++){
					(res[i][j]+=mat[i][k]*x[k][j])%=n;
				}
			}
		}
		return res;
	}
}ori[5]={31,37,41,43,47},bas[5]={31,37,41,43,47};
il void prework(int x){
	C[0][0]=1;
	for(int i=1;i<=mod[x];i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++){
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod[x];
		}
	}
	fac[0]=1;
	for(int i=1;i<=mod[x];i++){
		fac[i]=0;
		for(int j=0;j<i;j++){
			(fac[i]+=fac[j]*C[i-1][j])%=mod[x];
		}
	}
	for(int i=1;i<=mod[x];i++){
		ori[x][1][i]=fac[i-1];
	}
	bas[x][1][mod[x]]=bas[x][2][mod[x]]=1;
	for(int i=2;i<=mod[x];i++){
		bas[x][i][i-1]=1;
	}
}
il juz qpow(juz x,int y){
	juz res(x.n);
	for(int i=1;i<=x.n;i++){
		res[i][i]=1;
	}
	while(y){
		if(y&1){
			res=res*x;
		}
		x=x*x,y>>=1;
	}
	return res;
}
il void exgcd(int x,int y,int &p,int &q){
	if(!y){
		p=1,q=0;
		return ;
	}
	exgcd(y,x%y,p,q);
	int tmp=p;
	p=q;
	q=tmp-x/y*q;
}
il void solve(){
	cin>>n;
	for(int i=0;i<=4;i++){
		ans[i]=(ori[i]*qpow(bas[i],n))[1][1];
	}
	int Mod=1,res=0;
	for(int i=0;i<=4;i++){
		Mod*=mod[i];
	}
	for(int i=0,x,y;i<=4;i++){
		exgcd(Mod/mod[i],mod[i],x,y);
//		cout<<x<<"\n";
		res=(res+Mod/mod[i]*1ll*x%Mod*ans[i])%Mod;
	}
	cout<<(res%Mod+Mod)%Mod<<"\n";
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	for(int i=0;i<=4;i++){
		prework(i);
	}
	cin>>T;
	while(T--){
		solve();
	}
	return 0;
}
}
int main(){return asbt::main();}

C. 穿越广场

\(f_{i,j,x,y,0/1,0/1}\) 表示用了 \(i\) 个 D 和 \(j\) 个 R,第一个串匹配到了 \(x\) 位,第二个串匹配到了 \(y\) 位,两个串分别有没有匹配过的方案数。需要 kmp。发现空间存不下。考虑建立 AC 自动机,设 \(f_{i,j,k,0/1,0/1}\) 表示走到了 AC 自动机上的第 \(k\) 位即可方便地转移。具体实现时将后两个 \(0/1\) 合并成了一个。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int mod=1e9+7;
int T,n,m,tot,tr[205][2],fail[205];
int end[205],f[105][105][205][4];
string a,b;
queue<int> q;
il void insert(string x,int sta){
	int p=0;
	for(char c:x){
		int d=c=='D'?0:1;
		if(!tr[p][d]){
			tr[p][d]=++tot;
			tr[tot][0]=tr[tot][1]=0;
			fail[tot]=end[tot]=0;
		}
		p=tr[p][d];
	}
	end[p]|=sta;
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>m>>n>>a>>b;
		tot=0;
		tr[0][0]=tr[0][1]=0;
		fail[0]=end[0]=0;
		insert(a,1);
		insert(b,2);
		for(int d=0;d<=1;d++){
			if(tr[0][d]){
				q.push(tr[0][d]);
			}
		}
		while(q.size()){
			int u=q.front();
			q.pop();
			for(int d=0;d<=1;d++){
				if(tr[u][d]){
					fail[tr[u][d]]=tr[fail[u]][d];
					end[tr[u][d]]|=end[tr[fail[u]][d]];
					q.push(tr[u][d]);
				}
				else{
					tr[u][d]=tr[fail[u]][d];
				}
			}
		}
		memset(f,0,sizeof f); 
		f[0][0][0][0]=1;
		for(int i=0;i<=n;i++){
			for(int j=0;j<=m;j++){
				for(int k=0;k<=tot;k++){
					for(int x=0;x<=3;x++){
						if(!f[i][j][k][x]){
							continue;
						}
						(f[i+1][j][tr[k][0]][x|end[tr[k][0]]]+=f[i][j][k][x])%=mod;
						(f[i][j+1][tr[k][1]][x|end[tr[k][1]]]+=f[i][j][k][x])%=mod;
					}
				}
			}
		}
		int ans=0;
		for(int i=0;i<=tot;i++){
			(ans+=f[n][m][i][3])%=mod;
		}
		cout<<ans<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

D. 欢乐豆

如果一个点 \(u\) 不是被修改的某一条边的端点,那么它对答案的贡献就是 \((n-1)\times a_u\)。于是我们只用考虑被修改的点。

用修改的边连出若干连通块,那么连通块内的点到其它点的距离就是我们需要求的,对每个点都跑一边 dijkstra 即可。但值得注意的是有可能会先走出连通块,再走回来。于是我们还需要考虑连通块外 \(a\) 值最小的点。对于其它没有考虑的点,到它们的距离是和这个另加进来的点一样的。这样的时间复杂度是 \(O(m^3)\) 的。注意到有很大一部分松弛操作的更新值是相同的,使用线段树即可。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
#define pb push_back
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,maxm=6e3+5,inf=1e18;
int n,m,a[maxn],enm,hd[maxn],ans,p[maxn],dfn[maxn],idx[maxn],cnt,hp[maxn];
bool xiu[maxn],vis[maxn];
vector<pii> e[maxn];
struct{
	int v,nxt;
}E[maxm];
il void addedge(int u,int v){
	E[++enm].v=v;
	E[enm].nxt=hd[u];
	hd[u]=enm;
}
il void dfs(int u){
	xiu[u]=0,vis[u]=1;
	dfn[u]=++cnt;
	idx[cnt]=u;
	ans-=a[u]*(n-1);
	for(int i=hd[u];i;i=E[i].nxt){
		int v=E[i].v;
		if(xiu[v]){
			dfs(v);
		}
	}
}
int wei[maxm<<1],zhi[maxm<<1],tag[maxm<<1];
il void pushup(int id){
	if(~wei[lid]&&zhi[lid]<=zhi[rid]||wei[rid]==-1){
		wei[id]=wei[lid],zhi[id]=zhi[lid];
	}
	else{
		wei[id]=wei[rid],zhi[id]=zhi[rid];
	}
}
il void pushtag(int id,int v){
	zhi[id]=min(zhi[id],v),tag[id]=min(tag[id],v);
}
il void pushdown(int id){
	if(tag[id]<inf){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=inf;
	}
}
il void build(int id,int l,int r){
	wei[id]=l,zhi[id]=tag[id]=inf;
	if(l==r){
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
}
il void add(int id,int l,int r,int p,int v){
	if(l==r){
		zhi[id]+=v;
		return ;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	if(p<=mid){
		add(lid,l,mid,p,v);
	}
	else{
		add(rid,mid+1,r,p,v);
	}
	pushup(id);
}
il void clear(int id,int l,int r,int p){
	if(l==r){
		wei[id]=-1;
		return ;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	if(p<=mid){
		clear(lid,l,mid,p);
	}
	else{
		clear(rid,mid+1,r,p);
	}
	pushup(id);
}
il int query(int id,int l,int r,int p){
	if(l==r){
		return zhi[id];
	}
	pushdown(id);
	int mid=(l+r)>>1;
	return p<=mid?query(lid,l,mid,p):query(rid,mid+1,r,p);
}
il void solve(int l,int r){
	for(int i=1;i<=r;i++){
		build(1,l,r);
		add(1,l,r,i,-inf);
		while(~wei[1]){
			int p=wei[1],u=idx[wei[1]],t=zhi[1];
			clear(1,l,r,p);
			if(!p){
				ans+=t*(n-cnt);
				pushtag(1,t+a[u]);
			}
			else{
				for(pii j:e[u]){
					int tmp=query(1,l,r,dfn[j.fir]);
					hp[j.fir]=min(tmp,t+j.sec)-min(tmp,t+a[u]);
				}
				ans+=t;
				pushtag(1,t+a[u]);
				for(pii j:e[u]){
					if(hp[j.fir]){
						add(1,l,r,dfn[j.fir],hp[j.fir]);
					}
				}
			}
		}
	}
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ans+=a[i],p[i]=i;
	}
	ans*=n-1;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		e[u].pb(mp(v,w));
		addedge(u,v);
		addedge(v,u);
		xiu[u]=xiu[v]=1;
	}
	sort(p+1,p+n+1,[](const int &x,const int &y){return a[x]<a[y];});
	if(xiu[p[1]]){
		dfs(p[1]);
		int t=1;
		while(vis[t]){
			t++;
		}
		idx[0]=p[t];
		solve(!p[t],cnt);
	}
	for(int i=2;i<=n;i++){
		if(xiu[p[i]]){
			cnt=0;
			dfs(p[i]);
			idx[0]=p[1];
			solve(0,cnt);
		}
	}
	cout<<ans;
	return 0;
}
}
signed main(){return asbt::main();}
posted @ 2025-05-04 16:05  zhangxy__hp  阅读(54)  评论(0)    收藏  举报