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

image

A. 异或

\(r+l=n+1\) 的特殊性质提示一个做法:修改时只在顶点标记,用两个矩阵分别记录向下传递的数和向右下传递的数。于是一个小三角就等于一个大三角减掉一个小三角再减掉一个矩形。这两个三角也都是到底的,可以用如上方法计算;矩形直接做差分即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e3+5;
int n,m;
ll a[maxn][maxn],b[maxn][maxn],d[maxn][maxn];
int main(){
//	freopen("xor2.in","r",stdin);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	while(m--){
//		puts("666");
		int r,c,l,s;
		cin>>r>>c>>l>>s;
		a[r][c]+=s,b[r][c]+=s;
		a[r+l][c+l]-=s,b[r+l][c+l]-=s;
		d[r+l][c]-=s,d[r+l][c+l]+=s;
//		puts("777");
	}
//	puts("666");
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i+1][j+1]+=a[i][j];
			a[i+1][j]+=b[i][j];
			b[i+1][j]+=b[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1];
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			ans^=a[i][j]+d[i][j];
		}
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}

B. 游戏

考虑如果 Alice 每次都删去元素更多的那一半,则他最多操作 \(\log n\) 次就删完了,算上先后手就是 \(2\times\log n\) 次后答案小于等于 \(0\)。对于 Bob 也是类似的,即 \(2\times\log n\) 次后答案大于等于 \(0\)。于是对于 \(m>2\times\log n\) 的情况直接输出 \(0\) 即可,剩下的情况去做暴搜,时间复杂度为 \(O(nm)\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,a[maxn],b[maxn],hp[maxn];
il int dfs(int x,int l,int r){
	if(l>r){
		return 0;
	}
	if(x>m){
		int res=0;
		for(int i=l;i<=r;i++){
			res+=a[i];
		}
		return res;
	}
	int pl=l,pr=r;
	for(int i=l;i<=r;i++){
		hp[a[i]%b[x]?pl++:pr--]=a[i];
	}
	for(int i=l;i<=r;i++){
		a[i]=hp[i];
	}
	return x&1?min(dfs(x+1,l,pr),dfs(x+1,pl,r)):max(dfs(x+1,l,pr),dfs(x+1,pl,r));
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>b[i];
	}
	if(m>2*log2(n)){
		cout<<0;
		return 0;
	}
	cout<<dfs(1,1,n);
	return 0;
}
}
signed main(){return asbt::main();}

C. 连通块

首先边数比较爆炸,考虑优化建图。对于每个 \(a_i\),求出它所有的由 \(2\) 个质数相乘得到的因子,将 \(i\) 于这些点连边即可(对于每个 \(a_i\) 这样的点最多有 \(36\) 个)。

可以发现的一个性质是,那个删掉的点必然在最大的连通块里。我们要在连通块中找到删掉一个点后最大的子树,那么这就可以用圆方树来做。实际上是不用建出圆方树的,直接在 tarjan 的过程中统计即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=4e6+5,maxm=1e7+5;
int T,n,prn,prm[maxm],tot,mpf[maxm];
int hd[maxn],enm,hp1[15],hp2[15],ID[maxm];
int cnt,mx1,mx2,mp1,mp2,dfn[maxn],low[maxn],stk[maxn],top,ans,sz[maxn];
bool npr[maxm],vis[maxn];
struct{
	int v,nxt;
}e[maxn<<1];
il void addedge(int u,int v){
	e[++enm].v=v;
	e[enm].nxt=hd[u];
	hd[u]=enm;
}
il void Addedge(int u,int x){
//	cout<<u<<" "<<x<<"\n";
	if(!ID[x]){
		ID[x]=++tot;
	}
	int v=ID[x];
	addedge(u,v);
	addedge(v,u);
}
il void euler(int x=1e7){
	for(int i=2;i<=x;i++){
		if(!npr[i]){
			prm[++prn]=i;
			mpf[i]=i;
		}
		for(int j=1;j<=prn&&i*1ll*prm[j]<=x;j++){
			npr[i*prm[j]]=1;
			mpf[i*prm[j]]=prm[j];
			if(i%prm[j]==0){
				break;
			}
		}
	}
}
il void dfs(int u){
	vis[u]=1;
	if(u<=n){
		cnt++;
	}
	for(int i=hd[u],v;i;i=e[i].nxt){
		v=e[i].v;
		if(!vis[v]){
			dfs(v);
		}
	}
}
il void tarjan(int u,int fa){
	dfn[u]=low[u]=++cnt;
	stk[++top]=u,sz[u]=u<=n;
	int chl=0,res=0;
	bool ged=0;
	for(int i=hd[u],v;i;i=e[i].nxt){
		v=e[i].v;
		if(v==fa){
			continue;
		}
		if(!dfn[v]){
			tarjan(v,u);
			chl++;
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				if(fa||chl>1){
					ged=1;
				}
				int sum=0,x=0;
				do{
					x=stk[top--];
					sum+=sz[x],sz[u]+=sz[x];
				}while(x!=v);
				res=max(res,sum);
			}
		}
		else{
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(u<=n){
//		cout<<u<<"\n";
		if(ged){
			ans=min(ans,max(mx1-sz[u],res));
		}
		else{
			ans=min(ans,mx1-1);
		}
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	euler();
	cin>>T;
	while(T--){
		cin>>n;
		tot=n,enm=1;
		for(int i=1,x;i<=n;i++){
			cin>>x;
			int cnt=0;
			while(mpf[x]){
				hp1[++cnt]=mpf[x];
				while(x%hp1[cnt]==0){
					x/=hp1[cnt];
					hp2[cnt]++;
				}
			}
			for(int j=1;j<=cnt;j++){
				if(hp2[j]>1){
					Addedge(i,hp1[j]*hp1[j]);
				}
				for(int k=j+1;k<=cnt;k++){
					Addedge(i,hp1[j]*hp1[k]);
				}
			}
			for(int j=1;j<=cnt;j++){
				hp1[j]=hp2[j]=0;
			}
		}
		mx1=mx2=mp1=mp2=0;
		for(int i=1;i<=tot;i++){
			if(!vis[i]){
				cnt=0;
				dfs(i);
				if(cnt>=mx1){
					mx2=mx1,mp2=mp1;
					mx1=cnt,mp1=i;
				}
				else if(cnt>=mx2){
					mx2=cnt,mp2=i;
				}
			}
		}
		cnt=top=0,ans=mx1;
		tarjan(mp1,0);
		cout<<max(ans,mx2)<<"\n";
		for(int i=1;i<=tot;i++){
			dfn[i]=low[i]=hd[i]=vis[i]=0;
		}
		for(int i=1;i<=1e7;i++){
			ID[i]=0;
		}
	} // clear
	return 0;
}
}
int main(){return asbt::main();}

D. 公交路线

我们考虑对于每个点算出以它为某个端点的答案,相加得到总方案数(需要除以 \(4\))。

\(f_u\) 表示 \(u\) 的子树内经过 \(u\) 的路径数,\(g_u\) 表示所有经过 \(u\) 的路径数,二者可以对每种颜色建虚树,然后树上差分求出。然后再求一个 \(f\) 的根链和 \(pf\)

对于一条路径 \(u=u_1,u_2,\dots,u_p,w,v_q,v_{q-1},\dots,v_1=v\),其中 \(w\) 为 lca,与其相交的路径数即为 \(\sum_{i=1}^{p}f_i+\sum_{i=1}^{q}f_i+g_w\),即 \(pf_u+pf_v-2\times pf_w+g_w\)。于是对于 \(u\),它的答案即为 \(\sum\limits_{c_v=c_u}(\sum_{i=1}^{n}f_i-pf_u+pf_v-2\times pf_w+g_w)\)。其中前两项是好求的,剩下的三项再建虚树,上传到 \(w\) 上再下传即可。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,k,sf,c[maxn],f[maxn],pf[maxn],g[maxn],cg[maxn],ans[maxn];
vector<int> e[maxn],cun[maxn];
namespace LCA{
	int dfn[maxn],oula[maxn<<1],cnt,idx[maxn<<1];
	il void dfs(int u,int fa){
		dfn[u]=++cnt;
		oula[cnt]=cnt;
		idx[cnt]=u;
		for(int v:e[u]){
			if(v==fa){
				continue;
			}
			dfs(v,u);
			oula[++cnt]=dfn[u];
		}
	}
	struct{
		int Log[maxn<<1],st[maxn<<1][22];
		il void build(){
			for(int i=2;i<=cnt;i++){
				Log[i]=Log[i>>1]+1;
			}
			for(int i=1;i<=cnt;i++){
				st[i][0]=oula[i];
			}
			for(int j=1;j<=Log[cnt];j++){
				for(int i=1;i+(1<<j)-1<=cnt;i++){
					st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
				}
			}
		}
		il int query(int l,int r){
			int p=Log[r-l+1];
			return min(st[l][p],st[r-(1<<p)+1][p]);
		}
	}ST;
	il void init(){
		dfs(1,0),ST.build();
	}
	il int lca(int u,int v){
		if(dfn[u]>dfn[v]){
			swap(u,v);
		}
//		cout<<ST.query(dfn[u],dfn[v])<<"\n";
		return idx[ST.query(dfn[u],dfn[v])];
	}
}
using LCA::dfn;
using LCA::lca;
namespace vtr{
	int col,sum,tot,hp[maxn<<1],sz[maxn],spf[maxn];
	vector<int> e[maxn];
	il bool cmp(int x,int y){
		return dfn[x]<dfn[y];
	}
	il void build(){
		tot=0;
		for(int u:cun[col]){
			hp[++tot]=u;
		}
		hp[++tot]=1;
		sort(hp+1,hp+tot+1,cmp);
		for(int i=1;i<=sum;i++){
			hp[++tot]=lca(hp[i],hp[i+1]);
		}
		sort(hp+1,hp+tot+1,cmp);
		tot=unique(hp+1,hp+tot+1)-hp-1;
		for(int i=1;i<tot;i++){
			int x=lca(hp[i],hp[i+1]),y=hp[i+1];
			e[x].pb(y);
		}
	}
	il void clear(){
		for(int i=1;i<=tot;i++){
			e[hp[i]].clear();
		}
	}
	il void dfs1(int u,int fa){
		sz[u]=c[u]==col;
		for(int v:e[u]){
			dfs1(v,u);
			f[u]+=sz[v]*sz[u];
			sz[u]+=sz[v];
		}
		int tmp=sz[u]*(sum-sz[u]);
		cg[u]+=tmp,cg[fa]-=tmp;
	}
	il void dfs2(int u){
		if(c[u]==col){
			sz[u]=1,spf[u]=pf[u];
		}
		else{
			sz[u]=spf[u]=0;
		}
		for(int v:e[u]){
			dfs2(v);
			sz[u]+=sz[v],spf[u]+=spf[v];
		}
	}
	il void dfs3(int u,int w){
		int aisi=0,aifu=0;
		for(int v:e[u]){
			aisi+=sz[v];
			aifu+=spf[v];
		}
		if(c[u]==col){
			ans[u]+=w+2*pf[u]*aisi-g[u]*aisi-aifu;
			aisi++,aifu+=pf[u];
		}
		for(int v:e[u]){
			aisi-=sz[v],aifu-=spf[v];
			dfs3(v,w+2*pf[u]*aisi-g[u]*aisi-aifu);
			aisi+=sz[v],aifu+=spf[v];
		}
	}
	il void work1(int x){
		col=x,sum=cun[x].size();
		build();
		dfs1(1,0);
		clear(); // !!!
	}
	il void work2(int x){
		col=x,sum=cun[x].size();
		for(int u:cun[x]){
			ans[u]+=(sf-pf[u])*(sum-1);
		}
		build();
		dfs2(1);
		dfs3(1,0);
		clear();
	}
}
il void dfs(int u,int fa){
	pf[u]=pf[fa]+f[u];
	sf+=f[u];
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs(v,u);
		cg[u]+=cg[v];
	}
	g[u]=f[u]+cg[u];
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		cin>>c[i];
		cun[c[i]].pb(i);
	}
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	LCA::init();
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=n;j++){
//			cout<<i<<" "<<j<<" "<<lca(i,j)<<"\n";
//		}
//	}
//	cout<<"---------------------------------------\n";
	for(int i=1;i<=k;i++){
		if(cun[i].empty()){
			continue;
		}
		vtr::work1(i);
	}
	dfs(1,0);
//	for(int i=1;i<=n;i++){
//		cout<<i<<" "<<f[i]<<" "<<g[i]<<"\n";
//	}
	for(int i=1;i<=k;i++){
		if(cun[i].size()){
			vtr::work2(i);
		}
	}
	int res=0;
	for(int i=1;i<=n;i++){
		res+=ans[i];
	}
	res>>=2;
	cout<<res<<"\n";
	while(m--){
		int u;
		cin>>u;
		cout<<res-ans[u]<<"\n";
	}
	return 0;
}
}
signed main(){return asbt::main();}
posted @ 2025-07-05 20:44  zhangxy__hp  阅读(35)  评论(0)    收藏  举报