[BZOJ 3697]采药人的路径(点分治)

Description

采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。

Solution

一开始是按只有重心才作为休息站写的,写着写着发现并不是这样…后来看了黄学长的代码

好鬼畜的点分治QwQ

先把边权为0的变为-1,这样和为0就意味着阴阳平衡

h[i][1/0]表示之前的子树距离为i,是否存在休息站的路径的数量,g[i][1/0]表示这一子树距离为i,是否存在休息站的路径的数量

存在休息站与否可以用同一距离是否第二次出现来判定(这说明这一段的和一定是0)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
#define MAXN 100005
typedef long long LL;
using namespace std;
int n,root=0,tot=0,num,maxdeep,t[2*MAXN],siz[MAXN],maxv[MAXN],head[MAXN],cnt=0;
LL ans=0,g[MAXN*2][2],h[MAXN*2][2];
bool vis[MAXN];
struct Node1
{
    int next,to,w;
}Edges[MAXN*2];
int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void addedge(int u,int v,int w)
{
    Edges[++cnt].next=head[u];
    head[u]=cnt;
    Edges[cnt].to=v,Edges[cnt].w=w;
}
void getroot(int u,int f)
{
    siz[u]=1,maxv[u]=0;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(v==f||vis[v])continue;
        getroot(v,u);
        siz[u]+=siz[v],maxv[u]=max(maxv[u],siz[v]);
    }
    maxv[u]=max(maxv[u],tot-siz[u]);
    if(maxv[u]<maxv[root])root=u;
}
void getdeep(int u,int f,int d)
{
    maxdeep=max(maxdeep,abs(d-n));
    if(t[d])g[d][1]++;
    else g[d][0]++;
    t[d]++;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(v==f||vis[v])continue;
        getdeep(v,u,d+Edges[i].w);
    }
    t[d]--;
}
void work(int u)
{
    int mx=0;
    vis[u]=1,h[n][0]=1;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(vis[v])continue;
        maxdeep=1;
        getdeep(v,0,n+Edges[i].w);
        mx=max(mx,maxdeep);
        ans+=(h[n][0]-1)*g[n][0];
        for(int j=-maxdeep;j<=maxdeep;j++)
        ans+=h[n-j][1]*g[n+j][1]+h[n-j][0]*g[n+j][1]+h[n-j][1]*g[n+j][0];
        for(int j=n-maxdeep;j<=n+maxdeep;j++)
        {
            h[j][1]+=g[j][1];
            h[j][0]+=g[j][0]; 
            g[j][1]=g[j][0]=0;
        }
    }
    for(int i=n-mx;i<=n+mx;i++)h[i][0]=h[i][1]=0;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(vis[v])continue;
        root=0,tot=siz[v];
        getroot(v,u),work(root);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read();
    for(int i=1;i<n;i++)
    {
        int a=read(),b=read(),t=read();
        if(!t)t=-1;
        addedge(a,b,t),addedge(b,a,t);
    }
    maxv[0]=INF,tot=n;
    getroot(1,0),work(root);
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2017-06-04 19:00  Zars19  阅读(91)  评论(0编辑  收藏