zoukankan      html  css  js  c++  java
  • 洛谷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/+

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

  • 相关阅读:
    C#字符串(截取)
    字符串的截取(从指定位置)
    UVALive 7146 Defeat the Enemy(贪心+STL)(2014 Asia Shanghai Regional Contest)
    UVALive 7148 LRIP(树的分治+STL)(2014 Asia Shanghai Regional Contest)
    Google Code Jam Round 1A 2015 解题报告
    编程之美2015资格赛 解题报告
    ZOJ 3781 Paint the Grid Reloaded(BFS)
    【转】赞一下huicpc035
    【转】lonekight@xmu·ACM/ICPC 回忆录
    【转】[退役]纪念我的ACM——headacher@XDU
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9102717.html
Copyright © 2011-2022 走看看