[SDOI2008]洞穴勘测
Posted Tan_tan_tann
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2008]洞穴勘测相关的知识,希望对你有一定的参考价值。
洞穴勘测
题解
首先看到这道题应该很容易想到
L
C
T
LCT
LCT,但应该没人想打
L
C
T
LCT
LCT
我们看到题面后应该很容易想到并查集,但我们的操作有加边与删边,并查集没办法支持删边。
我们可以考虑将加边删边转化一下,并查集虽然不支持删边,但并查集是支持回退的。
所以说我们要让我们的加边删边变成一个可以回退的形式。
每一个时刻原图都保证是树或者森林,每条边加入后一直会保留到它被删除的时候,也就是说这条边存在的时间是一个连续的区间。
这就让我们很快想到了线段树,线段树上维护时间的区间,我们就将每条边加入到它对应的时间区间去。
此时,我们从线段树的根节点一直到线段树的叶子节点所加入的所有边构成的图就是这个叶子时刻所对应的图,从根节点到某个节点加入的所有边构成的图就是这个时间区间所共有的边构成的图。
我们只要在进入这个节点时加入边,离开这个节点时将加入的边删掉即可,并查集要用按秩合并的形式。
由于一条边最多会出现在
log
m
\\log\\,m
logm的区间中,所以我们总共会有
m
log
m
m\\log\\,m
mlogm个区间。
总时间复杂度
O
(
m
log
m
log
n
)
O\\left(m\\log\\,m\\log\\,n\\right)
O(mlogmlogn)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
#define MAXN 10005
#define MAXM 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=1000000000000000000LL;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=100000;
const int n1=520;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int fa[MAXN],sz[MAXN],n,m;
pii ask[MAXM],b[MAXM];bool vis[MAXM];
map<pii,int>mp;
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i,sz[i]=1;}
int findSet(int x){return fa[x]==x?x:findSet(fa[x]);}
class SegmentTree{
private:
vector<pii>tr[MAXM<<2];
public:
void insert(int rt,int l,int r,int al,int ar,pii aw){
if(l>r||l>ar||r<al)return ;int mid=l+r>>1;
if(al<=l&&r<=ar){tr[rt].push_back(aw);return ;}
if(al<=mid)insert(rt<<1,l,mid,al,ar,aw);
if(ar>mid)insert(rt<<1|1,mid+1,r,al,ar,aw);
}
void sakura(int rt,int l,int r){
int mid=l+r>>1,siz=tr[rt].size();
for(int i=0;i<siz;i++){
tr[rt][i].fir=findSet(tr[rt][i].fir);
tr[rt][i].sec=findSet(tr[rt][i].sec);
if(sz[tr[rt][i].fir]>sz[tr[rt][i].sec])
fa[tr[rt][i].sec]=tr[rt][i].fir,sz[tr[rt][i].fir]+=sz[tr[rt][i].sec];
else fa[tr[rt][i].fir]=tr[rt][i].sec,sz[tr[rt][i].sec]+=sz[tr[rt][i].fir];
}
if(l==r){
if(ask[l].fir+ask[l].sec)
puts(findSet(ask[l].fir)==findSet(ask[l].sec)?"Yes":"No");
}
else sakura(rt<<1,l,mid),sakura(rt<<1|1,mid+1,r);
for(int i=siz-1;i>=0;i--)
if(sz[tr[rt][i].fir]>sz[tr[rt][i].sec])
sz[tr[rt][i].fir]-=sz[tr[rt][i].sec],fa[tr[rt][i].sec]=tr[rt][i].sec;
else sz[tr[rt][i].sec]-=sz[tr[rt][i].fir],fa[tr[rt][i].fir]=tr[rt][i].fir;
}
}T;
signed main(){
read(n);read(m);makeSet(n);
for(int i=1;i<=m;i++){
char opt[10];int u,v;scanf("%s %d %d",opt,&u,&v);
if(u>v)swap(u,v);pii tmp=mkpr(u,v);
if(opt[0]=='C')mp[tmp]=i,b[i]=tmp;
else if(opt[0]=='D')
T.insert(1,1,m,mp[tmp],i,tmp),vis[mp[tmp]]=1;
else ask[i]=tmp;
}
for(int i=1;i<=m;i++)if(b[i].fir&&!vis[i])
T.insert(1,1,m,i,m,b[i]);
T.sakura(1,1,m);
return 0;
}
谢谢!!!
以上是关于[SDOI2008]洞穴勘测的主要内容,如果未能解决你的问题,请参考以下文章
LCTBZOJ2049 [SDOI2008]Cave 洞穴勘测