zoukankan      html  css  js  c++  java
  • luogu P2664 树上游戏 点分治

    可能是我有点菜,网上的题解都看不懂......但是写完之后看了好多题解代码,很多都有不影响正确性的思维上的小错误...

    回顾下点分治的基理,就是选定重心,把重心的答案和一些点对经过重心路径产生的答案全部都处理出来,从而使得所有子树再也没有任何关系,从而独立而分治。

    基本上O(nlogn),树上点对问题,都是点分治跑不了。

    我们考虑,我们选出重心后,我们显然可以用O(分块)的时间,处理出tot_ctb,表示所有点和根路径产生的答案。加到根的答案中。

    我们再考虑,怎么处理一些点对经过重心路径产生的答案全部都处理出来,从而使得所有子树再也没有任何关系,从而独立而分治。我们考虑,我们对根节点求出的tot_ctb和ctb[],如果能删去某一子树的影响,显然能够用来处理其他子树与该子树之间路径所产生的贡献。删去影响很简单,看代码即可。那怎么考虑产生的贡献呢,我们从子树的根开始搜到x,路上经过的颜色col,显然ctb[col],即根节点的其他子树通过颜色col产生的贡献,不应在对x及其子树产生影响了,同时反观,每一种颜色,都会从x及其子树出发,到其余子树的任何一个点,产生贡献,sub表示其余子树中点的数量。

    然后就ok啦,具体看代码。

      1 #include <cstdio>
      2 #include <algorithm>
      3 using namespace std;
      4 typedef long long ll;
      5 const int MAXN = 101000,inf = 100000000;
      6 int n,sum,maxn,cnt,root,num,sub;
      7 int col[MAXN],ctb[MAXN],head[MAXN],siz[MAXN],tot[MAXN],to[MAXN << 1],nxt[MAXN << 1];
      8 ll res[MAXN];
      9 ll tot_ctb;
     10 bool vis[MAXN];
     11 void add(int x,int y)
     12 {//常规链前 
     13     nxt[++cnt] = head[x];
     14     to[cnt] = y;
     15     head[x] = cnt;
     16 }
     17 void get_root(int x,int frm)
     18 {//常规重心 
     19     siz[x] = 1;
     20     int mx = 0;
     21     for (int i = head[x];i;i = nxt[i])
     22     {
     23         if (to[i] == frm || vis[to[i]] == true)
     24             continue;
     25         get_root(to[i],x);
     26         siz[x] += siz[to[i]];
     27         mx = max(mx,siz[to[i]]);
     28     }
     29     mx = max(mx,sum - siz[x]);
     30     if (mx < maxn)
     31     {
     32         maxn = mx;
     33         root = x;
     34     }
     35 }
     36 void dfs1(int x,int frm)
     37 {
     38     //维护出当前块的siz
     39     //维护出tot_ctb,所有子树及根节点本身对根节点答案的贡献 
     40     //维护出ctb[col],某种颜色col对根节点答案的贡献 
     41     siz[x] = 1;
     42     tot[col[x]]++;
     43     for (int i = head[x];i;i = nxt[i])
     44     {
     45         if (to[i] == frm || vis[to[i]] == true)
     46             continue;
     47         dfs1(to[i],x);
     48         siz[x] += siz[to[i]];
     49     }
     50     if (tot[col[x]] == 1)
     51     {//如果点x颜色是路径上第一次出现,显然x子树内,每个点到root都会因为x的颜色产生1贡献 
     52         tot_ctb += siz[x];
     53         ctb[col[x]] += siz[x];
     54     }
     55     tot[col[x]]--;
     56 }
     57 void change(int x,int frm,int val)
     58 {//清空某棵子树对ctb[],tot_ctb的影响 
     59     tot[col[x]]++;
     60     for (int i = head[x];i;i = nxt[i])
     61     {
     62         if (to[i] == frm || vis[to[i]] == true)
     63             continue;
     64         change(to[i],x,val);
     65     }
     66     if (tot[col[x]] == 1)
     67     {
     68         tot_ctb += siz[x] * val;
     69         ctb[col[x]] += siz[x] * val;
     70     }
     71     tot[col[x]]--;
     72 }
     73 void dfs2(int x,int frm)
     74 {//处理子树与当前块内其他点的所有影响,从而分治 
     75     tot[col[x]]++;
     76     if (tot[col[x]] == 1)
     77     {//如果某种颜色已经出现,那么显然这棵子树外的其他节点,不会对这里面的点答案产生贡献 
     78         tot_ctb -= ctb[col[x]];
     79         num++;
     80     }
     81     //num存放路径上出现颜色的个数,这些颜色均在子树外节点到当前点的路径上,产生了贡献 
     82     res[x] += tot_ctb + num * sub;
     83     for (int i = head[x];i;i = nxt[i])
     84     {
     85         if (to[i] == frm || vis[to[i]] == true)
     86             continue;
     87         dfs2(to[i],x);
     88     }
     89     if (tot[col[x]] == 1)
     90     {//复原tot_ctb和num 
     91         tot_ctb += ctb[col[x]];
     92         num--;
     93     }
     94     tot[col[x]]--;
     95 }
     96 void clear(int x,int frm)
     97 {//清空ctb[] 
     98     ctb[col[x]] = 0;
     99     for (int i = head[x];i;i = nxt[i])
    100     {
    101         if (to[i] == frm || vis[to[i]] == true)
    102             continue;
    103         clear(to[i],x);
    104     }
    105 }
    106 
    107 void solve(int x)
    108 {//注意这里的x是当前块的根 
    109     dfs1(x,0);//维护出tot_ctb,siz[],ctb[] 
    110     res[x] += tot_ctb;//我们直接把刚刚计算出的tot_ctb加到res[x]上 
    111     for (int i = head[x];i;i = nxt[i])
    112     {
    113         if (vis[to[i]] == true) 
    114             continue;
    115         //以下为了抹去子树to[i],对tot_ctb和ctb[]的影响 
    116         tot[col[x]]++;
    117         //在考虑根节点的情况下,定向搜索一颗子树做出的边界操作
    118         //因为我们之前是dfs1(x,0),这次是 change(to[i],0),tot[col[x]]需要特殊处理 
    119         tot_ctb -= siz[to[i]];
    120         //x的颜色产生的贡献也要去掉 
    121         ctb[col[x]] -= siz[to[i]];
    122         change(to[i],x,-1);
    123         tot[col[x]]--;
    124         sub = siz[x] - siz[to[i]];//sub表示这棵子树会对多少个节点产生影响 
    125         dfs2(to[i],x);//将子树to[i]与当前块内其他点的影响处理完,以便后续分治 
    126         //以下为了恢复子树to[i],对tot_ctb和ctb[]的影响 
    127         tot[col[x]]++;
    128         tot_ctb += siz[to[i]];
    129         ctb[col[x]] += siz[to[i]];
    130         change(to[i],x,1);
    131         tot[col[x]]--;
    132     }
    133     tot_ctb = 0;num = 0;//复原全局变量,下次备用 
    134     clear(x,0);//清空相关的ctb[] 
    135 }
    136 void pot_div(int x)
    137 {
    138     vis[x] = true;
    139     solve(x);
    140     for (int i = head[x];i;i = nxt[i])
    141     {
    142         if (vis[to[i]] == true) 
    143             continue;
    144         maxn = inf;
    145         sum = siz[to[i]];
    146         get_root(to[i],x);
    147         pot_div(root); 
    148     }
    149 }
    150 int main()
    151 {
    152     scanf("%d",&n);
    153     for (int i = 1;i <= n;i++)
    154         scanf("%d",&col[i]);
    155     int tx,ty;
    156     for (int i = 1;i <= n - 1;i++)
    157     {
    158         scanf("%d%d",&tx,&ty);
    159         add(tx,ty);
    160         add(ty,tx); 
    161     }
    162     maxn = inf;
    163     sum = n;
    164     get_root(1,0);
    165     pot_div(root);
    166     for (int i = 1;i <= n;i++)
    167         printf("%lld
    ",res[i]);
    168     return 0; 
    169 }
    心之所动 且就随缘去吧
  • 相关阅读:
    image
    MySQLdb
    【基础】Equal方法、面向对象-多态-继承-封装
    【转】SSL协议、SET协议、HTTPS简介
    【转】UML图与软件开发过程那点关系
    【转载】日本社会为啥没有“王思聪”
    调用0A中断输入字符串数据段的DUP定义
    字符串输入逆序输出(回车换行符)
    输入单个字符并输出
    仿射密码加密解密文件流
  • 原文地址:https://www.cnblogs.com/iat14/p/10553674.html
Copyright © 2011-2022 走看看