题解 洛谷 P4230 【连环病原体】

用双指针扫描来找环,加入 \(r\) 位置的边后,若形成了环,就删去 \(l\) 位置的边,直到环断掉,加边删边和判定连通性用 \(LCT\) 维护即可。

考虑如何计算环的贡献,对于区间 \([l,r]\),若其形成了环,则区间 \([l,r+1],[l,r+2] \dots [l,m]\) 都形成了环。得最终的贡献为区间 \([l,r]\) 都加上 \(m-r+1\),区间 \([r+1,m]\) 加上一个首项为 \(m-r\),公差为 \(-1\) 的等差数列。用线段树维护差分数列即可。

#include<bits/stdc++.h>
#define maxn 200010
#define maxm 800010
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int m,root=1,pos=1;
int fa[maxn],ch[maxn][2],rev[maxn];
ll sum[maxm],add[maxm],ans[maxn];
struct edge
{
    int x,y;
}e[maxn];
void pushadd(int cur,int l,int r,ll v)
{
    sum[cur]+=v*(r-l+1),add[cur]+=v;
}
void pushdown(int cur,int l,int r)
{
    if(!add[cur]) return;
    pushadd(ls,l,mid,add[cur]),pushadd(rs,mid+1,r,add[cur]),add[cur]=0;
}
void modify(int L,int R,int l,int r,ll v,int cur)
{
    if(L>R) return;
    if(L<=l&&R>=r)
    {
        pushadd(cur,l,r,v);
        return;
    }
    pushdown(cur,l,r);
    if(L<=mid) modify(L,R,l,mid,v,ls);
    if(R>mid) modify(L,R,mid+1,r,v,rs);
    sum[cur]=sum[ls]+sum[rs];
}
void dfs(int l,int r,int cur)
{
    if(l==r)
    {
        ans[l]=sum[cur];
        return;
    }
    pushdown(cur,l,r);
    dfs(l,mid,ls),dfs(mid+1,r,rs);
}
bool check(int x)
{
    return ch[fa[x]][1]==x;
}
bool notroot(int x)
{
    return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void pushrev(int x)
{
    rev[x]^=1,swap(ch[x][0],ch[x][1]);
}
void spread(int x)
{
    if(!rev[x]) return;
    pushrev(ch[x][0]),pushrev(ch[x][1]),rev[x]=0;
}
void rotate(int x)
{
    int y=fa[x],z=fa[y],k=check(x),w=ch[x][k^1];
    if(notroot(y)) ch[z][check(y)]=x;
    ch[x][k^1]=y,ch[y][k]=w;
    if(w) fa[w]=y;
    fa[x]=z,fa[y]=x;
}
void all(int x)
{
    if(notroot(x)) all(fa[x]);
    spread(x);
}
void splay(int x)
{
    all(x);
    for(int y;notroot(x);rotate(x))
        if(notroot(y=fa[x]))
            rotate(check(x)^check(y)?x:y);
}
void access(int x)
{
    for(int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y;
}
void makeroot(int x)
{
    access(x),splay(x),pushrev(x);
}
void split(int x,int y)
{
    makeroot(x),access(y),splay(y);
}
int findroot(int x)
{
    access(x),splay(x);
    while(ch[x][0]) x=ch[x][0];
    splay(x);
    return x;
}
void link(int x,int y)
{
    split(x,y),fa[x]=y;
}
void cut(int x,int y)
{
    split(x,y),fa[x]=ch[y][0]=0;
}
int main()
{
    read(m);
    for(int i=1;i<=m;++i) read(e[i].x),read(e[i].y);
    for(int i=1;i<=m;++i)
    {
        int x=e[i].x,y=e[i].y;
        while(findroot(x)==findroot(y))
        {
            modify(pos,pos,1,m,m-i+1,root);
            modify(i+1,m,1,m,-1,root);
            cut(e[pos].x,e[pos].y),pos++;
        }
        link(x,y);
    }
    dfs(1,m,root);
    for(int i=1;i<=m;++i)
        ans[i]+=ans[i-1],printf("%lld ",ans[i]);
    return 0;
}
posted @ 2020-08-09 20:57  lhm_liu  阅读(191)  评论(0编辑  收藏  举报