CF639F Bear and Chemistry
一、题目
二、解法
多练练这种大数据结构其实也挺好😕,那些比较难写的东西多写写就熟练了,话说这道题又是我自己想出来的。
题目就是要你判断这些点是否在一个强连通块内,可以转化成判断两点是否在强连通块内。
我们考虑原图的一个生成树,那么添加一条非树边就相当于把树上的一个环缩成一个点。但实际上不需要缩点,我们可以把这条路径上的边全部染色,那么如果两点的树上路径全是被染色的边就说明在一个强连通块内。
因为题目要求强制在线所以我们可以考虑一些在线的数据结构,比如 \(\tt lct\),考虑如果一条边连接了不同连通块内的点就直接合并,否则我们把路径 \(\tt split\) 出来置 \(0\),因为需要支持赋值标记的撤销,所以一开始需要保证所有边权都是 \(1\),也就是需要先缩点,建出缩点之后的生成树。
这道题的加密方式很离谱,题目中的 \(R\) 就是如果答案为 Yes
增加询问组数的大小。注意因为我们要把边变成点连上去,所以注意数据不要开小了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cassert>
#include <stack>
using namespace std;
const int M = 300005;
const int N = 1200005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,q,t,tot,ans,vi[M],ea[M],eb[M],ec[M];
int Ind,cnt,dfn[M],low[M],col[M],f[M],st[M],siz[N];
int sum[N],fl[N],cov[N],ch[N][2],fa[N],a[N],b[N];
struct edge{int v,next;}e[N];stack<int> s;
void dfs(int u,int fa)
{
low[u]=dfn[u]=++Ind;s.push(u);
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(!dfn[v])
{
dfs(v,i);
low[u]=min(low[u],low[v]);
}
else if(i^fa^1)
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
int v;++cnt;
do
{
v=s.top();s.pop();
col[v]=cnt;
}while(v^u);
}
}
//namespace : link-cut-tree
void up(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+a[x];
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+a[x]*b[x];
}
int chk(int x)
{
return ch[fa[x]][1]==x;
}
int nrt(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void flip(int x)
{
if(!x) return ;
swap(ch[x][0],ch[x][1]),fl[x]^=1;
}
void cover(int x,int y)
{
if(!x) return ;
cov[x]=b[x]=y;sum[x]=siz[x]*y;
}
void down(int x)
{
if(fl[x]) flip(ch[x][0]),flip(ch[x][1]),fl[x]=0;
if(cov[x]!=-1) cover(ch[x][0],cov[x])
,cover(ch[x][1],cov[x]),cov[x]=-1;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
ch[y][k]=w;fa[w]=y;
if(nrt(y)) ch[z][chk(y)]=x;fa[x]=z;
ch[x][k^1]=y;fa[y]=x;
up(y);up(x);
}
void splay(int x)
{
int y=x,z=0;st[++z]=y;
while(nrt(y)) st[++z]=y=fa[y];
while(z) down(st[z--]);
while(nrt(x))
{
y=fa[x];
if(nrt(y))
{
if(chk(x)==chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;x=fa[y=x])
splay(x),ch[x][1]=y,up(x);
}
void makert(int x)
{
access(x);splay(x);flip(x);
}
int findrt(int x)
{
access(x);splay(x);
while(ch[x][0]) down(x),x=ch[x][0];
splay(x);return x;
}
void split(int x,int y)
{
makert(x);access(y);splay(y);
}
void link(int x,int y)
{
assert(findrt(x)!=findrt(y));
makert(x);makert(y);
fa[x]=y;
}
void cut(int x,int y)
{
split(x,y);
fa[x]=ch[y][0]=0;up(y);
}
int ask(int x,int y)
{
split(x,y);return sum[y];
}
void add(int x,int y,int c)
{
split(x,y);cover(y,c);
}
int get()
{
int x=(read()+ans)%n;
return x==0?n:x;
}
signed main()
{
n=read();m=read();q=read();tot=1;
memset(cov,-1,sizeof cov);
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
}
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i,0);
t=cnt;
for(int u=1;u<=n;u++)
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v,x=col[u],y=col[v];
if(findrt(x)!=findrt(y))
{
t++;a[t]=b[t]=1;up(t);
link(x,t);link(y,t);
}
}
for(int Case=1;Case<=q;Case++)
{
int ni=read(),mi=read(),f=1;
for(int i=1;i<=ni;i++) vi[i]=col[get()];
for(int i=1;i<=mi;i++)
{
int u=col[get()],v=col[get()];
ea[i]=u;eb[i]=v;ec[i]=0;
if(findrt(u)!=findrt(v))
{
ec[i]=++t;a[t]=b[t]=1;up(t);
link(u,t);link(v,t);
}
else add(u,v,0);
}
for(int i=1;i<ni;i++)
if(findrt(vi[i])!=findrt(vi[i+1])
|| ask(vi[i],vi[i+1])>0)
{
f=0;
break;
}
if(f) puts("YES"),ans=(ans+Case)%n;
else puts("NO");
for(int i=mi;i>=1;i--)
{
int u=ea[i],v=eb[i];
if(ec[i]) cut(u,ec[i]),cut(v,ec[i]);
else add(u,v,1);
}
}
}