zoukankan      html  css  js  c++  java
  • BZOJ3697

    Portal

    Description

    给出一棵(n(nleq10^5))个点的树,每条边的边权为(-1)(1)。求有多少条无向路径((u,v))(exists kin(u,v)),满足((u,k)=0,(k,v)=0)

    Solution

    依然是点分治。
    记录(pre[0][d])表示在根及前若干个子树中,到根距离为(d)且是首次为(d)的点的个数;(pre[1][d])表示非首次距离为(d)的点的个数。(cur[0])(cur[1])相似的记录当前子树的信息。那么(ans)增加(cur[0][d] imes pre[1][-d] + cur[1][d] imes (pre[0][-d]+pre[1][-d]))。然后将(cur)加到(pre)中去。注意根是处在(pre[0][0])且在路径上是在所有点之前的,所以(cur[0][0])(cur[1][0])都应加到(pre[1][0])中。
    于是我们就可以统计出过根的满足条件的路径条数,分治下去即可。

    时间复杂度(O(nlogn))

    Code

    //采药人的路径
    #include <algorithm>
    #include <cstdio>
    using std::max;
    typedef long long lint;
    inline char gc()
    {
        static char now[1<<16],*s,*t;
        if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
        return *s++;
    }
    inline int read()
    {
        int x=0; char ch=gc();
        while(ch<'0'||'9'<ch) ch=gc();
        while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
        return x;
    }
    int const N=1e5+10;
    int const ZERO=1e5+10;
    int n;
    int h[N],cnt;
    struct edge{int v,w,nxt;} ed[N<<1];
    void edAdd(int u,int v,int w)
    {
        cnt++; ed[cnt].v=v,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt;
        cnt++; ed[cnt].v=u,ed[cnt].w=w,ed[cnt].nxt=h[v],h[v]=cnt;
    }
    lint ans;
    int G,siz0,siz[N],chMax[N]; bool vst[N];
    void solve(int u);
    void getG(int u,int fa)
    {
        siz[u]=1,chMax[u]=0;
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(vst[v]||v==fa) continue;
            getG(v,u); siz[u]+=siz[v],chMax[u]=max(chMax[u],siz[v]);
        }
        chMax[u]=max(chMax[u],siz0-siz[u]);
        if(chMax[u]<chMax[G]) G=u;
    }
    int pre[2][N<<1],cur[2][N<<1]; int dCnt[N<<1];
    int tCnt,t[N]; bool inT[N<<1];
    void getDst(int u,int fa,int d)
    {
        if(!inT[d]) t[++tCnt]=d,inT[d]=true;
        cur[dCnt[d]>0][d]++,dCnt[d]++;
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v,w=ed[i].w;
            if(vst[v]||v==fa) continue;
            getDst(v,u,d+w);
        }
        dCnt[d]--;
    }
    lint calc(int u,int d0)
    {
        tCnt=0; getDst(u,0,ZERO+d0);
        lint res=0;
        for(int i=1;i<=tCnt;i++)
        {
            int d=t[i];
            res+=(lint)pre[0][ZERO*2-d]*cur[1][d];
            res+=(lint)pre[1][ZERO*2-d]*(cur[0][d]+cur[1][d]);
        }
        for(int i=1;i<=tCnt;i++)
        {
            int d=t[i]; inT[d]=false;
            pre[d==ZERO][d]+=cur[0][d],cur[0][d]=0;
            pre[1][d]+=cur[1][d],cur[1][d]=0;
        }
        return res;
    }
    void reset(int u,int fa,int d)
    {
        pre[0][d]=pre[1][d]=0;
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(vst[v]||v==fa) continue;
            reset(v,u,d+ed[i].w);
        }
    }
    void DC(int u)
    {
        vst[u]=true; pre[0][ZERO]=1;
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(vst[v]) continue;
            if(siz[v]>siz[u]) siz[v]=siz0-siz[u];
            ans+=calc(v,ed[i].w);
        }
        reset(u,0,ZERO);
        for(int i=h[u];i;i=ed[i].nxt) {int v=ed[i].v; if(!vst[v]) solve(v);}
    }
    void solve(int u) {siz0=siz[u],G=0,getG(u,0),DC(G);}
    int main()
    {
        n=read();
        for(int i=1;i<=n-1;i++)
        {
            int u=read(),v=read(),w=read();
            edAdd(u,v,w?1:-1);
        }
        ans=0;
        siz[1]=n,chMax[0]=n,solve(1);
        printf("%lld
    ",ans);
        return 0;
    }
    

    P.S.

    看题时还有权限,写完就没了...

  • 相关阅读:
    codevs 2010 求后序遍历
    code vs 1013 求先序排列
    codevs 3143 二叉树的序遍历
    codevs 3083 二叉树
    找树的根和孩子
    1501 二叉树最大宽度和高度
    1758:二叉树
    sql 如何把查询得到的结果如何放入一个新表中
    2011的n次方
    计算2的N次方
  • 原文地址:https://www.cnblogs.com/VisJiao/p/BZOJ3697.html
Copyright © 2011-2022 走看看