CF1599I Desert / 原题链接
题意
仙人掌是一张无向连通图,在一个仙人掌上,任意一条边至多只会出现在一个环上。
荒漠是一张无向图,一个荒漠的每个极大连通分量都是一个仙人掌。
给定一个 \(n\) 个点 \(m\) 条边的无向图,求有多少对 \(l,r\in [1,m]\),使得只保留编号在 \([l,r]\) 中的边后,图变成荒漠。
数据范围:\(n\le 2.5\times 10^5,m\le 5\times 10^5\)
解法
转化一下题目,运用尺取法(Two-pointer),维护两个指针 \(l,r\),代表现在 \([l,r]\) 是合法的区间。那么 \(r\) 右移时,我们加边; \(l\) 右移时,我们删边。
然后就可以用 Link Cut Cactus 去维护加边、删边了。但是我不会 LCC,于是用 LCT 去维护。
LCT 不支持维护边的信息,将边拆点即可,设为 \(E\) 点。
LCT 上维护两个值 \(val=0/1,tot\),依次代表 \(E\) 点是否在某个环中,这个点的子树是否有在环中的 \(E\) 点。
加边
设加第 \(r\) 条边 \((x,y)\)。如果 \(x,y\) 不连通,直接加入即可。如果联通,查询 \(x,y\) 间是否有在环中的 \(E\) 点(即查询 \(x\sim y\) 的 Splay 根节点的 \(tot\))。
- *如果没有,那么加边后这些 \(E\) 点都变成环中边了。将它们的 \(val\) 设成 1,用 lazytag 维护。同时维护一个值 \(id\),代表 \(val=1\) 是来自于哪条边。即将 \(x\sim y\) 的 \(id:=r\),同样用 lazytag 维护,具体原因下文会讲。
- 如果有,那么加边会导致会有 \(E\) 点对应的边同时处于两个环中(一个是现在加的边,一个是以前让它的 \(val=1\) 的边),就不是荒漠了。不断地 \(l:=l+1\),删边,直到查询到这种情况不出现为止。
删边
设删第 \(l\) 条边 \((x,y)\)。如果 \(x,y\) 联通,那么就直接剪断。
但是这样做会有错误。如果以前 \(x,y\) 在一个环中,但是环边没有加入 LCT(上述带 * 的情况)那么会造成信息的丢失。我们把根的 \(id\) 对应的边再加入进 LCT 即可,同时要清零 \(id\)。
如果不连通,那么这个边加边时就是带 * 的情况。我们把 \(x\sim y\) 的 Splay, \(val:=0,id:=0\) 即可。用 lazytag 维护。
时间复杂度 \(O(nlogn)\)。(\(n\) 为拆点后的总点数)
代码
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define lsn(o) tre[o].son[0]
#define rsn(o) tre[o].son[1]
using namespace std;
const int n7=1012345,m7=n7;
struct dino{int x,y;}e[m7];
struct mist{int fa,son[2];int laz,id,lazid;bool val,tot,fp;}tre[n7];
int n,m;long long ans;
int rd(){
int shu=0;bool fu=0;char ch=getchar();
while( !isdigit(ch) ){if(ch=='-')fu=1;ch=getchar();}
while( isdigit(ch) )shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
return fu?-shu:shu;
}
void updat(int o){
tre[o].tot=(tre[lsn(o)].tot|tre[rsn(o)].tot);
if(o>n)tre[o].tot|=tre[o].val;
}
void pudown(int o){
if(tre[o].laz==1){
tre[lsn(o)].val=tre[rsn(o)].val=1;
tre[lsn(o)].tot=tre[rsn(o)].tot=1;
tre[lsn(o)].laz=tre[rsn(o)].laz=1;
}
if(tre[o].laz==-1){
tre[lsn(o)].val=tre[rsn(o)].val=0;
tre[lsn(o)].tot=tre[rsn(o)].tot=0;
tre[lsn(o)].laz=tre[rsn(o)].laz=-1;
}
tre[o].laz=0;
if(tre[o].lazid>0){
tre[lsn(o)].id=tre[rsn(o)].id=tre[o].lazid;
tre[lsn(o)].lazid=tre[rsn(o)].lazid=tre[o].lazid;
}
if(tre[o].lazid==-1){
tre[lsn(o)].id=tre[rsn(o)].id=0;
tre[lsn(o)].lazid=tre[rsn(o)].lazid=-1;
}
tre[o].lazid=0;
if(tre[o].fp){
tre[lsn(o)].fp^=1,tre[rsn(o)].fp^=1;
swap( lsn(o),rsn(o) );
tre[o].fp=0;
}
tre[0]=(mist){0,{0,0},0,0,0,0,0,0};
}
bool Dwhi(int o){return rsn(tre[o].fa)==o;}
bool izrot(int o){return lsn(tre[o].fa)==o||rsn(tre[o].fa)==o;}
void rota(int o){
int y=tre[o].fa,z=tre[y].fa,whi=Dwhi(o);
int fawhi=(izrot(y)?Dwhi(y):-1),v=tre[o].son[!whi];
tre[v].fa=y,tre[y].son[whi]=v;
tre[y].fa=o,tre[o].son[!whi]=y;
tre[o].fa=z;if(~fawhi)tre[z].son[fawhi]=o;
updat(y),updat(o);
}
void puall(int o){
if( izrot(o) )puall(tre[o].fa);
pudown(o);
}
void splay(int o){
puall(o);
while( izrot(o) ){
int y=tre[o].fa;
if( izrot(y) ){
Dwhi(o)==Dwhi(y)?rota(y):rota(o);
}
rota(o);
}
}
void aces(int o){
int las=0;
while(o){
splay(o),rsn(o)=las,updat(o);
las=o,o=tre[o].fa;
}
}
void Mroot(int o){
aces(o),splay(o),tre[o].fp^=1;
}
int Froot(int o){
aces(o),splay(o);
while( lsn(o) )pudown(o),o=lsn(o);
splay(o);
return o;
}
void split(int o1,int o2){
Mroot(o1),aces(o2),splay(o2);
}
void link(int o1,int o2){
if( Froot(o1)==Froot(o2) )return;
Mroot(o1),tre[o1].fa=o2;
}
bool cancut(int o1,int o2){
if( Froot(o1)^Froot(o2) )return 0;
split(o1,o2);
if( tre[o1].fa^o2 || lsn(o1) || rsn(o1) )return 0;
return 1;
}
void cut(int o1,int o2){
if( cancut(o1,o2) )tre[o1].fa=lsn(o2)=0,updat(o2);
}
int Gdot(int now){
pudown(now);
int o=lsn(now);
while(o){
if(o>n)return o;
else pudown(o),o=rsn(o);
}
o=rsn(now);
while(o){
if(o>n)return o;
else pudown(o),o=lsn(o);
}
return 0;
}
bool check(int o1,int o2){
if( Froot(o1)^Froot(o2) )return 1;
split(o1,o2);int o=Gdot(o2);splay(o);
if(!tre[o].tot)return 1;
return 0;
}
int main(){
n=rd(),m=rd();
rep(i,1,m)e[i]=(dino){rd(),rd()};
for(int l=1,r=0;r<=m;++r){
ans=ans+r-l+1;
// printf("!%d %d\n",l,r);
if(r==m)break;
while( !check(e[r+1].x,e[r+1].y) ){
if( !cancut(e[l].x,l+n) ){
split(e[l].x,e[l].y);
int o=Gdot(e[l].y);
if(o){
splay(o);
tre[o].val=tre[o].tot=0,tre[o].laz=-1;
tre[o].id=0,tre[o].lazid=-1;
}
}
else{
int z=tre[l+n].id;
cut(e[l].x,l+n),cut(l+n,e[l].y);
if(z){
link(e[z].x,z+n),link(z+n,e[z].y);
split(e[l].x,e[l].y);
int o=Gdot(e[l].y);
if(o){
splay(o);
tre[o].val=tre[o].tot=0,tre[o].laz=-1;
}
}
}
l++;
}
if( Froot(e[r+1].x)==Froot(e[r+1].y) ){
split(e[r+1].x,e[r+1].y);
int o=Gdot(e[r+1].y);
if(o){
splay(o);
tre[o].val=tre[o].tot=1,tre[o].laz=1;
tre[o].id=tre[o].lazid=r+1;
}
}
else{
link(e[r+1].x,r+1+n),link(r+1+n,e[r+1].y);
}
}
printf("%lld",ans);
return 0;
}