洛谷4230:连环病原体——题解

https://www.luogu.org/problemnew/show/P4230

题面还是太难复制了。

朴素的搜环显然是O(n^2)的。

但是可以发现搜环会反复的走过很多边以及我们只需要一个环就够了,所以方案之间是有重叠的。

(这时候yxd神犇看了眼题并且5s切了这道题。)

那么还是固定枚举左端点l,右端点r后移直到有环为止,则合法区间为[l,r][l,r+1]……[l,m],而且下一次的搜索显然只需要l++就可以继承上次的方案。

这个工作LCT即可胜任,显然每个边也就被遍历过一次,所以是O(mlogn)的。

现在的问题是我们要如何计数,差分也许能给我们灵感。

对于[l,r][l,r+1]……[l,m]其中的每个区间都要+1,我们分成两份,一份是提取所有区间的公共区间[l,r]用差分简单做到存储,另一份就是剩下的那些区间了,很显然他们构成了等差数列。

显然对数列我们差分无济于事,考虑既然等差那就对公差差分。

比如3 2 1这样的序列差分之后得到的就是3 -4 0 0 1,运算方法就是从右往左扫,记录当前的公差sum,则s[i]=s[i+1]+sum

(orz一眼秒的rabbithu)

#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=4e5+5;
const int M=2e5+5;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int u,v;
}e[M];
int m,r,rev[N],tr[N][2],fa[N],q[N];
inline bool get(int x){
    return tr[fa[x]][1]==x;
}
inline bool isroot(int x){
    if(!fa[x])return 1;
    return tr[fa[x]][0]!=x&&tr[fa[x]][1]!=x;
}
inline void push(int x){
    if(!rev[x])return;
    swap(tr[x][0],tr[x][1]);
    if(tr[x][0])rev[tr[x][0]]^=1;
    if(tr[x][1])rev[tr[x][1]]^=1;
    rev[x]=0;
}
inline void rotate(int x){
    int y=fa[x],z=fa[y],which=get(x);
    if(z&&!isroot(y))tr[z][tr[z][1]==y]=x;
    tr[y][which]=tr[x][which^1];fa[tr[y][which]]=y;
    fa[y]=x;tr[x][which^1]=y;fa[x]=z;
}
inline void splay(int x){
    q[r=0]=x;
    for(int y=x;!isroot(y);y=fa[y])q[++r]=fa[y];
    for(int i=r;i>=0;i--)push(q[i]);
    while(!isroot(x)){
    if(!isroot(fa[x]))
        rotate(get(x)==get(fa[x])?fa[x]:x);
    rotate(x);
    }
}
inline void access(int x){
    for(int y=0;x;y=x,x=fa[x]){
    splay(x);tr[x][1]=y;
    if(y)fa[y]=x;
    }
}
inline int findroot(int x){
    access(x);splay(x);
    while(push(x),tr[x][0])x=tr[x][0];
    splay(x);
    return x;
}
inline void makeroot(int x){
    access(x);splay(x);rev[x]^=1;
}
inline void link(int x,int y){
    makeroot(x);fa[x]=y;
}
inline void cut(int x,int y){
    makeroot(x);access(y);splay(y);
    tr[y][0]=0;fa[x]=0;
}
inline bool pan(int x,int y){
    return findroot(x)==findroot(y);
}
ll s1[M],s2[M];
int main(){
    m=read();
    for(int i=1;i<=m;i++)
    e[i].u=read(),e[i].v=read();
    int l=1,r=0;
    for(l;l<=m;l++){
    bool ok=0;
    while(r<m){
        r++;
        if(pan(e[r].u,e[r].v)){ok=1;break;}
        link(e[r].u,e[r].v);
    }
    if(ok){
        s1[l]+=m-r+1;
        if(r<m)s1[r+1]-=m-r+1;
        s2[m]++;s2[r]+=r-m-1;
        if(r>1)s2[r-1]+=m-r;
        r--;
    }
    cut(e[l].u,e[l].v);
    }
    for(int i=1;i<=m;i++)s1[i]+=s1[i-1];
    ll sum=0;
    for(int i=m;i>=1;i--){
    sum+=s2[i];
    s2[i]=s2[i+1]+sum;
    }
    for(int i=1;i<=m;i++)printf("%lld ",s1[i]+s2[i]);
    puts("");
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-05-28 22:35  luyouqi233  阅读(181)  评论(0编辑  收藏  举报