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;
}
后言:
真难为我这点语文写作能力了......