zoukankan      html  css  js  c++  java
  • [国家集训队][bzoj 2152] 聪聪可可 [点分治]

    题面:

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

    思路:

    题目要求统计书上路径信息,想到树上分治算法

    实际上这是一道点分治裸题,我就不瞎BB思路了,直接上做法。

    对于一个节点,设dis[i]为其他节点到这个节点的距离 MOD 3

    那么可以证明,一条经过该节点的合法路径(i,j),( dis[i] + dis[j] ) % 3==0

    因此对每个节点求出其子树内的dis,经过该点的路径数即为(dis[i]==1的点数)*(dis[i]==2的点数)*2 + (dis[i]==0的点数)^2

    递归求解

    递归进入一个节点u时,ans先加入calc(u,0),即为上述求解过程(dis[u]==0开始)。

    每一次对于当前递归到的节点的每一棵子树,ans先减去dis(v,e[i].w),即为从(dis[v]==e[i].w)开始,为的是去掉重复计算的部分。

    求这个子树的重心

    方法:

    递归进入子树中每一个节点,统计其子最大的子树大小(包括连向其父节点的那一棵)

    这个最大值最小的节点就是树的重心。

    如果每次从这里进入,那么进入的子树的size一定小于整棵当前树的size的一半

    这样递归下去最多log n层

    求完这棵子树的重心以后从这个重心递归进入,求该子树的解

    最后ans即为答案数目(ans初始为0)

    Code:

     1 // luogu-judger-enable-o2
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<algorithm>
     5 #include<cstring>
     6 using namespace std;
     7 void _swap(int &x,int &y){x^=y;y^=x;x^=y;}
     8 int _max(int x,int y){return (x>y)?x:y;}
     9 inline int read(){
    10     int re=0,flag=1;char ch=getchar();
    11     while(ch>'9'||ch<'0'){
    12         if(ch=='-') flag=-1;
    13         ch=getchar();
    14     }
    15     while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    16     return re*flag;
    17 }
    18 int n,cnt,ans,dis[20010],first[20010],tmp[5],root,siz[20010],son[20010],sum;
    19 bool vis[20010];
    20 struct edge{
    21     int to,next,w;
    22 }a[40010];
    23 inline void add(int u,int v,int w){
    24     a[++cnt]=(edge){v,first[u],w};first[u]=cnt;
    25     a[++cnt]=(edge){u,first[v],w};first[v]=cnt;
    26 }
    27 int gcd(int x,int y){return (y?gcd(y,x%y):x);};
    28 void getroot(int u,int f){
    29     //cout<<"getroot "<<u<<" "<<f<<"
    ";
    30     int i,v;
    31     siz[u]=1;son[u]=0;
    32     for(i=first[u];~i;i=a[i].next){
    33         v=a[i].to;
    34         if(v==f||vis[v]) continue;
    35         getroot(v,u);
    36         siz[u]+=siz[v];
    37         son[u]=_max(son[u],siz[v]);
    38     }
    39     son[u]=_max(son[u],sum-siz[u]);//统计父亲节点的那棵子树,sum为当前的整棵子树的size
    40     //cout<<"finish getroot "<<son[u]<<'
    ';
    41     if(son[u]<son[root]) root=u;
    42 }
    43 void gettmp(int u,int f){
    44     //cout<<"gettmp "<<u<<" "<<f<<"
    ";
    45     int i,v;
    46     tmp[dis[u]]++;
    47     for(i=first[u];~i;i=a[i].next){
    48         v=a[i].to;
    49         if(v==f||vis[v]) continue;
    50         //cout<<" to "<<v<<'
    ';
    51         dis[v]=(dis[u]+a[i].w)%3;
    52         gettmp(v,u);
    53     }
    54 }
    55 int calc(int u,int d){//即为文中描述的calc
    56     dis[u]=d%3;tmp[0]=tmp[1]=tmp[2]=0;
    57     gettmp(u,0);
    58     return tmp[1]*tmp[2]*2+tmp[0]*tmp[0];
    59 }
    60 void dfs(int u){
    61     //cout<<"dfs "<<u<<"
    ";
    62     int i,v;
    63     vis[u]=1;ans+=calc(u,0);
    64     for(i=first[u];~i;i=a[i].next){
    65         v=a[i].to;
    66         if(vis[v]) continue;
    67         ans-=calc(v,a[i].w);
    68         sum=siz[v];root=0;//更新root和sum
    69         getroot(v,u);
    70         dfs(root);
    71     }
    72 }
    73 int main(){
    74     // freopen("cckk.in","r",stdin);
    75     // freopen("cckk.out","w",stdout);
    76     memset(first,-1,sizeof(first));
    77     int i,t1,t2,t3;
    78     n=read();
    79     for(i=1;i<n;i++){
    80         t1=read();t2=read();t3=read();
    81         add(t1,t2,t3);
    82     }
    83     //cout<<"finish read in
    ";
    84     sum=n;son[0]=n;//将son[0]初始化为极大值
    85     getroot(1,0);
    86     dfs(1);
    87     int div=gcd(n*n,ans);//注意约分
    88     printf("%d/%d",ans/div,n*n/div);
    89 }
  • 相关阅读:
    洛谷P2089 烤鸡
    HDU-1000 A+B Problem
    《新标准C++程序设计》4.7-4.9(C++学习笔记17)
    《新标准C++程序设计》4.6(C++学习笔记16)
    面向对象程序设计寒假作业3
    《新标准C++程序设计》4.5(C++学习笔记15)
    《新标准C++程序设计》4.4(C++学习笔记14)
    《新标准C++程序设计》4.2-4.3(C++学习笔记13)
    洛谷题解P1047 校门外的树
    [lr] 矫正白平衡
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8391928.html
Copyright © 2011-2022 走看看