SP9577
前言
可能大多数人看到这题就一眼 LCT 秒了。
但是我发现自己已经不会敲 LCT 了……
于是,提供一种离线做法。
思路
线段树分治。
考虑到每条边都有一个寿命,即从某时刻到某时刻里存在,我们具备了线段树分治的一个先决条件。特别的,如果最后都没有死,我们最后杀掉它。
我们维护一个时光序列,即某时刻中有的边,那么对每条边,我们在时光序列的某段中同时插入,最后对时光序列每个询问点进行查询即可,使用并查集复杂度是 \(O(nm\alpha(n))\) 的。
这不就暴力吗。
考虑到时光序列可以形如一颗 Leafy Tree (把实际信息存在叶子节点的树)的,我们用线段树维护它,查询时 dfs 一遍线段树即可。
什么,你问我 dfs 时怎么统计贡献?
使用带撤销并查集即可。
什么,你不会带撤销并查集?
复杂度 \(O(n+m\log m\log n)\)。
Code
带撤销并查集使用按秩合并没有精神,以下代码利用 Treap 的思想,封装了一个 Heap_DSU
。
// Problem: SP9577 DYNACON1 - Dynamic Tree Connectivity
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/SP9577
// Memory Limit: 1.46 MB
// Time Limit: 395 ms
#include <algorithm>
#include <map>
#include <stdio.h>
#include <vector>
typedef long long llt;
typedef unsigned uint;typedef unsigned long long ullt;
typedef bool bol;typedef char chr;typedef void voi;
typedef double dbl;
template<typename T>bol _max(T&a,T b){return(a<b)?a=b,true:false;}
template<typename T>bol _min(T&a,T b){return(b<a)?a=b,true:false;}
template<typename T>T power(T base,T index,T mod){return((index<=1)?(index?base:1):(power(base*base%mod,index>>1,mod)*power(base,index&1,mod)))%mod;}
template<typename T>T lowbit(T n){return n&-n;}
template<typename T>T gcd(T a,T b){return b?gcd(b,a%b):a;}
template<typename T>T lcm(T a,T b){return(a!=0||b!=0)?a/gcd(a,b)*b:(T)0;}
template<typename T>T exgcd(T a,T b,T&x,T&y){if(!b)return y=0,x=1,a;T ans=exgcd(b,a%b,y,x);y-=a/b*x;return ans;}
template<typename T,typename rng>
class Heap_DSU //人类智慧势能并查集
{
private:
std::vector<T>Fath,Used,H;
public:
Heap_DSU(){Fath.clear(),Used.clear(),H.clear();}
voi bzr(T n)
{
Fath.clear(),Used.clear(),H.clear();
for(T i=0;i<n;i++)Fath.push_back(i),H.push_back(rng()());
}
T Time(){return Used.size();}
T find(T p){return Fath[p]==p?p:find(Fath[p]);}
bol connected(T a,T b){return find(a)==find(b);}
bol merge(T a,T b)
{
a=find(a),b=find(b);
if(a==b)return Used.push_back(a),false;
if(H[a]<H[b])std::swap(a,b);
Fath[b]=a,Used.push_back(b);
return true;
}
bol revoke()
{
if(Used.empty())return false;
T p=Used.back();Used.pop_back();
if(p==Fath[p])return false;
Fath[p]=p;return true;
}
};
typedef std::pair<uint,uint>Pair;
struct Seg
{
Seg*L,*R;uint len;std::vector<Pair>Way;
voi build(uint n){L=R=NULL,Way.clear();if((len=n)>1)L=new Seg,R=new Seg,L->build(len>>1),R->build(len-(len>>1));}
voi insert(uint l,uint r,Pair p)
{
if(l>=r)return;
if(!l&&r==len){Way.push_back(p);return;}
if(l<(len>>1))
{
if(r<=(len>>1))L->insert(l,r,p);
else L->insert(l,len>>1,p),R->insert(0,r-(len>>1),p);
}
else R->insert(l-(len>>1),r-(len>>1),p);
}
};
uint a=1919810;const uint b=10007,c=114513;
class rng{public:uint operator()(){return a=a*b+c;}};
Seg S;Heap_DSU<uint,rng>U;uint cnt;
bol Q[100005],Ans[100005];
uint A[100005],B[100005];
voi dfs(Seg*S)
{
uint t=0;for(auto p:S->Way){t++,U.merge(p.first,p.second);}
if(S->len==1)
{
if(Q[cnt])Ans[cnt]=U.connected(A[cnt],B[cnt]);
cnt++;
}
else dfs(S->L),dfs(S->R);
while(t--)U.revoke();
}
std::map<Pair,uint>M;
voi turn(uint&a,uint&b){if(a>b)std::swap(a,b);}
int main()
{
chr op[5];uint n,m,u,v;scanf("%u%u",&n,&m),U.bzr(n),S.build(m);
for(uint t=0;t<m;t++)
{
scanf("%s%u%u",op,&u,&v),turn(--u,--v);
switch(*op)
{
case'a':M[Pair(u,v)]=t;break;
case'r':S.insert(M[Pair(u,v)],t,Pair(u,v)),M.erase(Pair(u,v));break;
case'c':Q[t]=true,A[t]=u,B[t]=v;
}
}
for(auto s:M)S.insert(s.second,m,s.first);
dfs(&S);
for(uint t=0;t<m;t++)if(Q[t])puts(Ans[t]?"YES":"NO");
return 0;
}
本文来自博客园,作者:myee,转载请注明原文链接:https://www.cnblogs.com/myee/p/Luogu-solution-sp9577.html