CSP-S模拟19

前言:

一场 没有大样例 别开生面的模拟赛......

\(T1:\)染色(color)

题意:

给一个长度为\(n\)的序列染色,使得对于任意的\(1≤i<j≤n\),若\(|i-j|\)为质数,则\(i\)\(j\)不同色。求出颜色尽可能少的染色方案。

思路:

一道规律题。众所周知,在质数内,只有\(2\)为偶数,其余质数都为奇数。所以我们可以考虑奇偶染色,即\(12121212...\),但是又因为有\(2\),所以我们将\(4\)作为一个循环节的长度,即\(123412341234...\)。但是显然,当\(n\)小于\(8\)时,我们根本凑不齐\(2\)整个循环节,没有必要填\(4\)种颜色,这样是不优的。因此我们可以填\(11223344\)(至于为什么是这个序列?自己手模一下就知道了。)

代码:

$凤凰栖于梧桐木$
#include<iostream>
using namespace std;
const int N=1e4+5;
int n,col[N]; 
int main(){
//	freopen("color.in","r",stdin);
//	freopen("color.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>n;
	if(n>6){
		cout<<4<<'\n';
		for(int i=1,cnt=1;i<=n;i++,cnt++){
			cout<<cnt<<" ";
			cnt%=4;//4位一循环 
		}
		return 0;
	}//n大于6 
	cout<<((n+1)>>1)<<'\n';//小于6 
	if(n%2==0) for(int i=1,cnt=1;i<=n;i+=2,++cnt) cout<<cnt<<" "<<cnt<<" ";
	else 
		for(int i=1,cnt=1;i<=n;i+=2,cnt++){
			if(i==n) cout<<cnt;
			else cout<<cnt<<" "<<cnt<<" ";
		}
	return 0;
}

\(T2:\)序列(array)

没xiáo会,等我xiáo会了再说

改不动了,先贺一份

代码:

$忽闻人间祈神舞$

\(T3:\)树上询问(query)

题意:

给定一棵\(n\)个节点的树\(T\),每次询问给两个整数\(l\),\(r\),问存在多少个整数\(k\)使得从\(l\)沿着\(l→r\) 的简单路径走\(k\)步恰好到达\(k\)

思路:

一般人肯定都会想到把路径拆分为两条链,\(l→lca(l,r)\)\(lca(l,r)→r\)。然后我们一步一步往上跳,遇到满足条件的点就把答案加一。一点都不难嘛,写个\(dfs\)把图固定根节点变成成树,再求\(lca\),最后在线跑一边路径就好了。\(But......\)你会发现你光荣的\(TLE\)了,所以要考虑优化,怎么优化?不难发现,在\(l→lca(l,r)\)上符合条件的点为\(dep[l]-dep[x]=x\),可以化为\(dep[x]+x=dep[l]\),在\(lca(l,r)→r\)上符合条件的点为\(dep[l]-dep[lca]+dep[x]-dep[lca]=x\),可化简为\(dep[x]-x=2*dep[lca]-dep[l]\)。然后我们发现等号右边的我们可以预处理,因此我们将询问离线下来,然后使用树上前缀和差分预处理右边的式子,最后再跑一遍\(dfs\)统计答案就好啦~~

代码:

$口衔美玉踏晨露$
#include<iostream>
#include<vector>
using namespace std;
const int N=3e6+5;
int m,n,u,v,cnt,l[N],r[N],head[N],dep[N],fa[N],f[N],vis[N],dis[2][N<<1],lca[N],ans[N];
struct node{int to,nxt;}e[N];
struct wutong{int v,id;};
struct jade{int val,ch,dif,id;};
vector<wutong> q1[N];vector<jade> q2[N];
inline void add(int x,int y){
	e[++cnt].to=y;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}//建边 
inline int find(int x){
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}//找祖宗 
inline void dfs1(int bb,int daddy){
	dep[bb]=dep[daddy]+1;f[bb]=daddy;
	for(int i=head[bb];i;i=e[i].nxt){
		int y=e[i].to;
		if(y!=daddy) dfs1(y,bb);
	}
}//固定树 
inline void dfs2(int bb,int daddy){
	vis[bb]=1;
	for(int i=head[bb];i;i=e[i].nxt){
		int y=e[i].to;
		if(y!=daddy){
			dfs2(y,bb);
			fa[y]=bb;
		}
	}
	for(auto y:q1[bb]) if(vis[y.v]) lca[y.id]=find(y.v);
}//预处理lca 
inline void dfs3(int bb,int daddy){
	dis[0][dep[bb]+bb]++;//前半段 
	dis[1][dep[bb]-bb+n]++;//后半段 
	for(auto y:q2[bb]) ans[y.id]+=y.dif*dis[y.ch][y.val];//差分统计答案 
	for(int i=head[bb];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==daddy) continue;
		dfs3(y,bb);
	}
	dis[0][dep[bb]+bb]--;
	dis[1][dep[bb]-bb+n]--;
}
int main(){
//	freopen("query.in","r",stdin);
//	freopen("query.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++) fa[i]=i;//初始化 
	for(int i=1;i<n;i++){
		cin>>u>>v;
		add(u,v);add(v,u);//建边 
	}
	for(int i=1;i<=m;i++){
		cin>>l[i]>>r[i];
		q1[l[i]].push_back({r[i],i});
		q1[r[i]].push_back({l[i],i});//把询问离线下来 
	}dfs1(1,0);dfs2(1,0);//预处理 
	for(int i=1;i<=m;i++){
		q2[l[i]].push_back({dep[l[i]],0,1,i});
		q2[lca[i]].push_back({dep[l[i]],0,-1,i});//前半段 
		q2[r[i]].push_back({2*dep[lca[i]]-dep[l[i]]+n,1,1,i});
		if(lca[i]>1) q2[f[lca[i]]].push_back({2*dep[lca[i]]-dep[l[i]]+n,1,-1,i});//后半段 
	}dfs3(1,0);
	for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';//输出答案 
	return 0;
}//关于dfs中的变量名纯为恶趣味 如果不懂且想懂的话建议去问港城的朋友~~ 

\(T4:\)网络(network)

题意:

题面太长了,不想打了,就这样吧。

思路:

题目要求最后不小于\(\lfloor \frac{n}{2} \rfloor\),因此我们可以将所有的电线两两分为一组,若是奇数条导线,则最后一根导线单独为一组。我们不难发现可以使每一组的导线一个有电,一个没电。然后我们可以互换任意两组之间的两根导线(有点像同源染色体的非姐妹染色单体的互换?不过这里不限于同源染色体?差不多意思吧【论满天乱飞的联想】)。然后再根据互换后的状态倒推前面的状态

\(For~example:\)原本是\((x,u)\)\((v,y)\),互换后变为\((x,y)\)\((u,v)\)。若\(u\)中无电流,则\(x\)中有电流,\(v\)中有电流,\(y\)中无电流;反之同理。

据此,我们就可以写出代码啦~~(是不是很抽象?我也觉得是)

代码:

$赐福万千烟火处$
#include<iostream>
using namespace std;
const int N=5e5+5,M=5e6+5;
int m,n,ans[M],E[M],last[M][2];bool mk[N];pair<int,int> p[M];
int main(){
//	freopen("network.in","r",stdin);
//	freopen("network.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=m;i++) cin>>p[i].first>>p[i].second;//输入电线 
	for(int i=1;i<=n;i+=2) E[i]=i+1,E[i+1]=i;//两两分组 
	if(n&1) E[n]=n;//奇数个最后一个自己为一组 
	for(int i=1;i<=m;i++){
		int x=p[i].first,y=p[i].second;
		if(x==E[x]){
			int v=E[y];
			E[v]=v;E[x]=y;E[y]=x;
			last[i][0]=0;last[i][1]=v;
		}else if(y==E[y]){
			int u=E[x];
			E[u]=u;E[x]=y;E[y]=x;
			last[i][0]=u;last[i][1]=0;//单个的与其他组的互换 
		}else{
			int u=E[x],v=E[y];
			E[u]=v;E[v]=u;E[x]=y;E[y]=x;
			last[i][0]=u;last[i][1]=v;
		}//完整的两组之间的互换 
	}//互换 
	for(int i=1;i<=m;i++) if(i<E[i]) mk[i]=1;//标记每组中较小的那个为有电的( ?) 
	for(int i=m;i>=1;i--){
		int x=p[i].first,y=p[i].second;
		if(mk[y]) ans[i]=1;
		else ans[i]=0;//记录答案 
		int u=last[i][0],v=last[i][1];
		if(!u) mk[v]=1,mk[y]=0,mk[x]=1;
		else if(!v) mk[u]=1,mk[y]=1,mk[x]=0;
		else{
			if(mk[u]) mk[x]=0,mk[y]=1;
			if(mk[v]) mk[x]=1,mk[y]=0;
		}//倒推状态 
	}cout<<"YES"<<'\n';
	for(int i=1;i<=m;i++) cout<<ans[i];//输出答案 
	return 0;
}

后言:

真难为我这点语文写作能力了......

posted @ 2025-09-10 18:19  晏清玖安  阅读(43)  评论(2)    收藏  举报