題面:https://www.luogu.com.cn/problem/CF639F
題意:給一張無向圖(不保證聯通),每次選定一些點,
並在圖中加一些邊,問加邊后這些點能否在一個邊雙內。
\(n\),\(m\),\(\sum\)n1,\(\sum\)m1\(\leq\) \(3e5\)。
題解:
顯然,一個暴力的想法是每次建邊后跑一次邊雙。
復雜度\(O(Q*(n+m))\)
考慮如何優化。發現每次詢問時可能只需要幾個點,但我們
卻對整張圖跑了一次,不夠優秀。
考慮先對原圖邊雙縮點,得到一個森林。
每次詢問,可以按照給出的點和邊建出虛樹,然后在新圖上再跑邊雙。
這里建虛樹可能不好做,這里給出我的建樹過程:
1.將選中的點和新邊連接的兩點加入一個數組;
2.將這些點按照dfs序排序(需要預先對森林的每個連通塊做樹剖,
每次記錄dfs序的sum無需清零),這樣一個連通塊內的點是
數組里的連續一段;
3.對於同一聯通塊的點,正常建虛樹即可;對於不同連通塊的點,
先將上一個連通塊的虛樹建好(把棧清空),再建新連通塊的虛樹。
注意:
1.每次做完一定要清空;
2.注意強制在線,建議到CF上查看此題的強制在線方式,本代碼
中去掉了強制在線,防抄襲~
3.代碼較長時,一定要多加調試,多進行驗證輸出。
時間復雜度:\(O((n+m)logn)\),如果用st表求lca可以做到\(O(n+m)\)。
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
#define T e[k].to
int n,m,Q,X,Y,R,_n,_m,sum,now;
int d[303000],ned[303000],a[303000];
struct Graph{
vector<int>v[303000];
int root[303000],dep[303000],id[303000],son[303000],siz[303000],top[303000],ma[303000];
int head[303000],tot;
int vis[303000],dfn[303000],low[303000],clr[303000],color,mill;
int sn;
stack<int>s;
struct E{
int to,nt;
}e[606000];
I addin(int x,int y){v[y].emplace_back(x);}
I add(int x,int y){
e[++tot].to=y;
e[tot].nt=head[x];
head[x]=tot;
}
I tarjan(int x,int fa){
dfn[x]=low[x]=++mill;
vis[x]=1;s.emplace(x);
for(re k=head[x];k!=-1;k=e[k].nt){
if(k==(fa^1))continue;
if(!dfn[T]){
tarjan(T,k);
low[x]=min(low[T],low[x]);
}
else if(vis[T])low[x]=min(low[x],dfn[T]);
}
if(dfn[x]==low[x]){
++color;
while(s.top()!=x)clr[s.top()]=color,vis[s.top()]=0,s.pop();
clr[x]=color;vis[x]=0;s.pop();
}
}
I D_1(int x,int fa,int depth,int Root){
ma[x]=fa;dep[x]=depth;son[x]=-1;root[x]=Root;siz[x]=1;
re maxi=-1;
for(auto p:v[x]){
if(p==fa)continue;
D_1(p,x,depth+1,Root);
siz[x]+=siz[p];
if(maxi<siz[p])maxi=siz[p],son[x]=p;
}
}
I D_2(int x,int fa,int topi){
top[x]=topi;id[x]=++sum;
if(son[x]!=-1)D_2(son[x],x,topi);
for(auto p:v[x]){
if(p==fa||p==son[x])continue;
D_2(p,x,p);
}
}
IN ques_lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=ma[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
return x;
}
I build(){
tot=-1;C(head,-1);
F(i,1,m){
read(X);read(Y);add(X,Y);add(Y,X);
}
F(i,1,n)if(!dfn[i])tarjan(i,-1);
F(i,1,n)for(re k=head[i];k!=-1;k=e[k].nt)if(clr[i]!=clr[T])addin(clr[i],clr[T]);
F(i,1,color)if(!root[i])D_1(i,0,1,i),D_2(i,0,i);
}
I solve(){
sn=1;
F(i,1,now)if(!dfn[a[i]])tarjan(a[i],-1);
F(i,2,_n)
if(clr[d[i]]!=clr[d[1]]){
sn=0;
break;
}
if(sn)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
F(i,1,now)head[a[i]]=-1,dfn[a[i]]=low[a[i]]=clr[a[i]]=ned[a[i]]=0,mill=color=0;
}
}G,g;
inline bool bbb(int x,int y){return G.id[x]<G.id[y];}
int q[303000],r;
int main(){
read(n);read(m);read(Q);sum=0;C(g.head,-1);
G.build();
F(t,1,Q){
read(_n);read(_m);sum=0;g.tot=-1;
F(i,1,_n){
read(d[i]);d[i]=G.clr[d[i]];if(!ned[d[i]])ned[d[i]]=1,a[++sum]=d[i];
}
F(i,1,_m){
read(X);read(Y);X=G.clr[X];Y=G.clr[Y];
g.add(X,Y);g.add(Y,X);
if(!ned[X])ned[X]=1,a[++sum]=X;
if(!ned[Y])ned[Y]=1,a[++sum]=Y;
}
sort(a+1,a+1+sum,bbb);r=0;now=sum;
F(i,1,sum){
if(!r||G.root[q[r]]!=G.root[a[i]]){
while(r>1)g.add(q[r],q[r-1]),g.add(q[r-1],q[r]),r--;
q[r=1]=G.root[a[i]];
if(!ned[G.root[a[i]]])ned[G.root[a[i]]]=1,a[++now]=G.root[a[i]];
else continue;
}
re lca=G.ques_lca(q[r],a[i]);
while(G.id[q[r]]>G.id[lca]){
if(G.id[q[r-1]]>G.id[lca])g.add(q[r-1],q[r]),g.add(q[r],q[r-1]),r--;
else g.add(lca,q[r]),g.add(q[r],lca),q[r]=lca,a[++now]=lca;
}
q[++r]=a[i];
}
while(r>1)g.add(q[r],q[r-1]),g.add(q[r-1],q[r]),r--;
g.solve();
}
return 0;
}
