Bzoj3697 采药人的路径

 

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 949  Solved: 323
[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。

 

Source

 

树 点分治

tag上写着点分治,就按点分治做了,不然可能会用树上DP

之后在别的博客看到这么一句话:

点分治的题目和树规的题目很像,都是在树上乱搞,但点分治一般和路径更相关,如果用树规做会感觉冗余状态太多,内存和时间都无法承受,如果不用动态规划,直接在原树上运用这道题的方法,又无法保证时间复杂度(点分治让我们的算法对原树的形态依赖更小,可以保证时间复杂度)。   ——idy002  http://www.cnblogs.com/idy002/p/4374089.html

 

“阴““阳"权值分别记为1和-1,当一条路径权值和为0时,就说明平衡了

当选定一个点为根时,统计它的子树信息:用f[i][0/1]表示路径权值为i,无/有休息站的路径数目,g[i][0/1]表示该根已经统计过的子树中的(同左边)数目

为了防止计算中下标出现负数,将所有的权值统一加一个bas值(这里选择+n)

则ans+=(g[n][0]-1)*f[n][0] + g[n-j][1]*f[n+j][1]+g[n-j][0]*f[n+j][1]+g[n-j][1]*f[n+j][0]   ( mxdeep<=j<=mxdeep)

代码原本打算仿黄学长的简洁风格写,WA了之后改呀改,渐渐就变成几乎相同了

  1 /*by SilverN*/
  2 #include<algorithm>
  3 #include<iostream>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<cmath>
  7 #include<vector>
  8 #define LL long long
  9 using namespace std;
 10 const int mxn=200010;
 11 int read(){
 12     int x=0,f=1;char ch=getchar();
 13     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 14     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
 15     return x*f;
 16 }
 17 struct edge{
 18     int v,nxt,w;
 19 }e[mxn<<1];
 20 int hd[mxn],mct=0;
 21 void add_edge(int u,int v,int w){
 22     e[++mct].nxt=hd[u];e[mct].v=v;e[mct].w=w;hd[u]=mct;return;
 23 }
 24 LL ans=0;
 25 LL f[mxn][2];//根节点的所有已处理子树中,路径和为[i]的终点数目 
 26 LL g[mxn][2];//当前子树和为[i]且[有/无休息站]的终点 
 27 int t[mxn];
 28 int n,root;
 29 int mc[mxn],sum;
 30 int dep[mxn],size[mxn],mxdeep;
 31 int dis[mxn];
 32 bool vis[mxn];
 33 void DFS_sz(int u,int fa){
 34     size[u]=1;
 35     mc[u]=0;
 36     for(int i=hd[u];i;i=e[i].nxt){
 37         int v=e[i].v;
 38         if(v==fa || vis[v])continue;
 39         DFS_sz(v,u);
 40         size[u]+=size[v];
 41         if(size[v]>mc[u])mc[u]=size[v];
 42     }
 43     mc[u]=max(mc[u],sum-size[u]);
 44     if(mc[u]<mc[root])root=u;//更新重心 
 45     return;
 46 }
 47 void calc(int u,int fa){
 48     mxdeep=max(mxdeep,dep[u]);
 49     if(t[dis[u]])f[dis[u]][1]++;
 50     else f[dis[u]][0]++;
 51     ++t[dis[u]];
 52     for(int i=hd[u];i;i=e[i].nxt){
 53         int v=e[i].v;
 54         if(vis[v] || v==fa)continue;
 55         dep[v]=dep[u]+1;
 56         dis[v]=dis[u]+e[i].w;
 57         calc(v,u);
 58     }
 59     --t[dis[u]];
 60     return;
 61 }
 62 void DFS(int x){
 63     vis[x]=1;g[n][0]=1;
 64     int mx=0;
 65     for(int i=hd[x];i;i=e[i].nxt){//统计以x为根的子树答案 
 66         int v=e[i].v;
 67         if(vis[v])continue;
 68         dep[v]=1;dis[v]=n+e[i].w;//dis统一加n,防止出现负数 
 69         mxdeep=1;
 70         calc(v,0);
 71         mx=max(mx,mxdeep);
 72         ans+=(g[n][0]-1)*f[n][0];//以x结点为休息站而平衡的方案数(要减去结点x本身 )
 73         for(int j=-mxdeep;j<=mxdeep;j++){
 74             ans+=g[n-j][1]*f[n+j][1]+g[n-j][0]*f[n+j][1]+g[n-j][1]*f[n+j][0];
 75         }
 76         int ed=n+mxdeep;
 77         for(int j=n-mxdeep;j<=ed;j++){
 78             g[j][0]+=f[j][0];
 79             g[j][1]+=f[j][1];
 80             f[j][0]=f[j][1]=0;
 81         }
 82     } 
 83     int ed=n+mx;
 84     for(int j=n-mx;j<=ed;j++){//初始化 
 85         g[j][0]=g[j][1]=0;
 86     }
 87     for(int i=hd[x];i;i=e[i].nxt){
 88         int v=e[i].v;
 89         if(!vis[v]){
 90             root=0;
 91             sum=size[v];
 92             DFS_sz(v,0);
 93             DFS(root);
 94         }
 95     } 
 96     return;
 97 }
 98 int main(){
 99     int i,j;
100     int u,v,w;
101     n=read();
102     for(i=1;i<n;i++){
103         u=read();v=read();w=read();if(!w)w=-1;
104         add_edge(u,v,w);
105         add_edge(v,u,w);
106     }
107     sum=mc[0]=n;
108     DFS_sz(1,0);//统计子树尺寸
109     DFS(root);
110     printf("%lld\n",ans);
111     return 0;
112 }

 

posted @ 2017-02-15 18:26  SilverNebula  阅读(272)  评论(0编辑  收藏  举报
AmazingCounters.com