• 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
    ",ans);
    111     return 0;
    112 }
  • 相关阅读:
    启动Tomcat报错:严重: Exception starting filter struts2 java.lang.NoClassDefFoundError: org/apache/commons/lang/StringUtils
    数学 交换律
    android中LayoutInflater的使用
    数学 三角函数
    数学 矩阵
    数学 弧度
    数学 分配律
    Android开发必备的免费资源和工具
    数学 结合律
    View.setTag()的作用
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6402734.html
Copyright © 2020-2023  润新知