• BZOJ3697:采药人的路径(点分治)


    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

    Solution

    我可能学了假的点分治……
    用g[i][0…1],f[i][0…1]分别表示
    前面几个子树以及当前子树路径长度和为i的路径数目
    0和1用于区分路径上是否存在前缀和为i的节点(也就是可以设立中转站的节点)
     那么当前子树的贡献就是f[0][0] * g[0][0] + Σf [i][0] * g [-i][1] + f[i][1] * g[-i][0] + f[i][1] * g[-i][1],
    其中i的范围[-d,d],d为当前子树的深度。

    Code

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #define N (200000+100)
      5 using namespace std;
      6 struct node
      7 {
      8     int to,next,len;
      9 } edge[N*2];
     10 int n,k,sum,root,INF;
     11 long long ans,g[N][2],f[N][2];
     12 int t[N*2];
     13 int head[N],num_edge,max_depth;
     14 int depth[N],d[N],size[N],maxn[N],dis[N];
     15 bool vis[N];
     16 
     17 void add(int u,int v,int l)
     18 {
     19     edge[++num_edge].to=v;
     20     edge[num_edge].len=l;
     21     edge[num_edge].next=head[u];
     22     head[u]=num_edge;
     23 }
     24 
     25 void Get_root(int x,int fa)
     26 {
     27     size[x]=1;
     28     maxn[x]=0;
     29     for (int i=head[x]; i!=0; i=edge[i].next)
     30         if (!vis[edge[i].to] && edge[i].to!=fa)
     31         {
     32             Get_root(edge[i].to,x);
     33             size[x]+=size[edge[i].to];
     34             maxn[x]=max(maxn[x],size[edge[i].to]);
     35         }
     36     maxn[x]=max(maxn[x],sum-size[x]);
     37     if (maxn[x]<maxn[root]) root=x;
     38 }
     39 
     40 void Calc(int x,int fa)
     41 {
     42     max_depth=max(max_depth,depth[x]);
     43     if (t[dis[x]]) f[dis[x]][1]++;
     44     else f[dis[x]][0]++;
     45     t[dis[x]]++;
     46     for (int i=head[x]; i!=0; i=edge[i].next)
     47         if (edge[i].to!=fa && !vis[edge[i].to])
     48         {
     49             depth[edge[i].to]=depth[x]+1;
     50             dis[edge[i].to]=dis[x]+edge[i].len;
     51             Calc(edge[i].to,x);
     52         }
     53     t[dis[x]]--;
     54 }
     55 
     56 void Solve(int x)
     57 {
     58     vis[x]=true;
     59     g[n][0]=1;//关于这里为什么初始化为1的问题,想了好久,最后还是学姐给我的解答
     60     //http://blog.csdn.net/wu_tongtong/article/details/79428928
     61     //可能路径是从当前树根到某一个节点的时候,路径已经平衡,不需要去另一个子树里面找另一个链拼接了
     62     //所以这一部分的答案也要统计进去。
     63     int up=0;
     64     for (int i=head[x]; i!=0; i=edge[i].next)
     65         if (!vis[edge[i].to])
     66         {
     67             depth[edge[i].to]=1;
     68             dis[edge[i].to]=edge[i].len+n;
     69             max_depth=1;
     70             Calc(edge[i].to,0);
     71             up=max(up,max_depth);
     72 
     73             ans+=(g[n][0]-1)*f[n][0];
     74             for (int j=-max_depth; j<=max_depth; ++j)
     75                 ans+=g[n-j][0]*f[n+j][1]+g[n-j][1]*f[n+j][0]+g[n-j][1]*f[n+j][1];
     76             for (int j=-max_depth; j<=max_depth; ++j)
     77             {
     78                 g[n-j][0]+=f[n-j][0];
     79                 g[n-j][1]+=f[n-j][1];
     80                 f[n-j][0]=f[n-j][1]=0;
     81             }
     82         }
     83     for (int i=-up; i<=up; ++i)
     84         g[n-i][0]=g[n-i][1]=0;
     85     for (int i=head[x]; i!=0; i=edge[i].next)
     86         if (!vis[edge[i].to])
     87         {
     88             sum=size[edge[i].to];
     89             root=0;
     90             Get_root(edge[i].to,0);
     91             Solve(root);
     92         }
     93 }
     94 
     95 int main()
     96 {
     97     int u,v,l;
     98     scanf("%d",&n);
     99     sum=maxn[0]=n;
    100     for (int i=1; i<=n-1; ++i)
    101     {
    102         scanf("%d%d%d",&u,&v,&l);
    103         add(u,v,l==0?-1:1);
    104         add(v,u,l==0?-1:1);
    105     }
    106     Get_root(1,0);
    107     Solve(root);
    108     printf("%lld",ans);
    109 }
  • 相关阅读:
    linux文件管理之查找
    linux文件管理之管道与重定向
    linux文件管理之proc文件系统
    linux文件管理之bash shell
    linux进程管理之作业控制
    linux进程管理之优先级
    linux进程管理之信号控制
    Inno Setup制作最简单的安装程序
    intel汇编笔记
    WINDOWS程序设计对话框加载显示bmp图像及刷新
  • 原文地址:https://www.cnblogs.com/refun/p/8684130.html
Copyright © 2020-2023  润新知