bzoj千题计划248:bzoj3697: 采药人的路径

http://www.lydsy.com/JudgeOnline/problem.php?id=3697

 

点分治

路径0改为路径-1

g[i][0/1] 和 f[i][0/1]分别表示当前子树 和 已经处理完的兄弟节点子树 中,路径前缀和为i,前面是否还有一个前缀和为i的点

合法的路径分为三类:

1、路径以当前分治重心为端点,ans+=g[0][1]

2、路径跨越分治重心,休息站在分治重心,ans+=g[0][0]*f[0][0]

3、路径跨越分治重心,休息站不在分治重心,ans+= Σ g[i][0]*f[-i][1] + g[i][1]*f[-i][0] + g[i][1]*f[-i][1]  i∈[min_dis,max_dis]

 

#include<cstdio>
#include<iostream>

#define N 100001

using namespace std;

typedef long long LL;
 
int n;
int tot,front[N],to[N<<1],nxt[N<<1],val[N<<1];

int root,min_size;
int siz[N],mx[N];

bool vis[N];

LL g[N<<1][2],f[N<<1][2];
int cnt[N<<1];
int max_dis,min_dis;

LL ans;

void read(int &x)
{
     x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w;
}

void get_size(int x,int fa)
{
    siz[x]=1; mx[x]=0;
    for(int i=front[x];i;i=nxt[i])
        if(!vis[to[i]] && to[i]!=fa)
        {
            get_size(to[i],x);
            siz[x]+=siz[to[i]]; 
            if(siz[to[i]]>mx[x]) mx[x]=siz[to[i]];
        }
}

void get_root(int x,int pa,int fa)
{
    mx[x]=max(mx[x],siz[pa]-siz[x]);
    if(mx[x]<min_size) min_size=mx[x],root=x;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa && !vis[to[i]]) get_root(to[i],pa,x);
}  

void get_sum(int x,int fa,int d)
{
    if(d>max_dis) max_dis=d; 
    if(d<min_dis) min_dis=d;
    cnt[d+n]++;
    if(cnt[d+n]==1) g[d+n][0]++;
    else g[d+n][1]++;
    for(int i=front[x];i;i=nxt[i])
        if(!vis[to[i]] && to[i]!=fa) get_sum(to[i],x,d+val[i]);
    cnt[d+n]--;
}    

void work(int x,int pa)
{
    min_size=n+1;
    get_size(x,0);
    get_root(x,x,0);
    int maxn=-n,minn=n;
    for(int i=front[root];i;i=nxt[i])
        if(!vis[to[i]])
        {     
            max_dis=-n; 
            min_dis=n;
            get_sum(to[i],root,val[i]); 
            if(max_dis>maxn) maxn=max_dis;
            if(min_dis<minn) minn=min_dis;
            ans+=g[n][1];
            ans+=g[n][0]*f[n][0];
            for(int j=min_dis;j<=max_dis;++j) 
                ans+=g[j+n][0]*f[-j+n][1]+g[j+n][1]*f[-j+n][0]+g[j+n][1]*f[-j+n][1];
            for(int j=min_dis;j<=max_dis;++j)
            {
                f[j+n][0]+=g[j+n][0];
                f[j+n][1]+=g[j+n][1];
                g[j+n][0]=g[j+n][1]=0;
            }
        }
    for(int i=minn;i<=maxn;++i) f[i+n][0]=f[i+n][1]=0;
    vis[root]=true; 
    int rt=root;
    for(int i=front[root];i;i=nxt[i])
        if(!vis[to[i]]) work(to[i],rt);
}

int main()
{
//    freopen("data.in","r",stdin);
//    freopen("my.out","w",stdout); 
    read(n);
    int u,v,w;
    for(int i=1;i<n;++i)
    {
        read(u); read(v); read(w);
        if(!w) w=-1;
        add(u,v,w);
    }
    work(1,0);
    cout<<ans;
}

 

3697: 采药人的路径

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1505  Solved: 515
[Submit][Status][Discuss]

Description

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

Input

第1行包含一个整数N。
接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。

Output

输出符合采药人要求的路径数目。

Sample Input

7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1

Sample Output

1

HINT

 

对于100%的数据,N ≤ 100,000。

posted @ 2018-02-25 21:05  TRTTG  阅读(128)  评论(0编辑  收藏