BZOJ3697 - 采药人的路径

Portal

Description

给出一棵\(n(n\leq10^5)\)个点的树,每条边的边权为\(-1\)\(1\)。求有多少条无向路径\((u,v)\)\(\exists k\in(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]\times pre[1][-d] + cur[1][d] \times (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\n",ans);
    return 0;
}

P.S.

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

posted @ 2018-04-04 09:05  VisJiao  阅读(200)  评论(0编辑  收藏  举报