• 洛谷P2664 树上游戏(点分治)


    传送门

    题解

      因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直以为只有动态点分会很难没想到一般点分都这么可啪……%%%大佬

      我们考虑一下,对于一棵树,我们要处理的是子树对根的答案的贡献,以及经过根的路径的贡献(也就是$LCA$为根的点对的答案)。

      对于树中的一个点$i$,如果$i$的颜色是在$i$到根的路径上第一次出现,那么所有与$i$的$LCA$为根的点,都能与$i$的子树形成点对,且$i$的颜色会对那些点产生产生贡献$size[i]$(先不考虑其他子树中是否有相同颜色)。

      那么我们考虑$dfs$一遍整棵树,并记录,所有颜色产生的贡献$col[i]$以及贡献总和$sum$。然后$dfs$子树,并把其中的所有颜色的贡献给减掉。然后考虑子树中的一个点,设$x$为到根上的所有点的贡献总和,$num$表示根到节点上的颜色个数,$y=size[root]-size[该子树]$,那么$ans[j]+=sum-x+num*size[y]$

      然后最后记得加上对根节点的答案的贡献$ans[root]+=sum-col[根的颜色]+size[root]$

      不得不说思路真的挺妙的

      1 //minamoto
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<cstring>
      5 #define ll long long
      6 using namespace std;
      7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
      8 char buf[1<<21],*p1=buf,*p2=buf;
      9 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
     10 inline int read(){
     11     #define num ch-'0'
     12     char ch;bool flag=0;int res;
     13     while(!isdigit(ch=getc()))
     14     (ch=='-')&&(flag=true);
     15     for(res=num;isdigit(ch=getc());res=res*10+num);
     16     (flag)&&(res=-res);
     17     #undef num
     18     return res;
     19 }
     20 char sr[1<<21],z[20];int C=-1,Z;
     21 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
     22 inline void print(ll x){
     23     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
     24     while(z[++Z]=x%10+48,x/=10);
     25     while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
     26 }
     27 const int N=200005;
     28 int head[N],Next[N<<1],ver[N<<1],c[N],son[N],sz[N],size,cnt[N];
     29 ll col[N],ans[N],much,sum,num;
     30 int tot,n,rt;bool vis[N];
     31 inline void add(int u,int v){
     32     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
     33     ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
     34 }
     35 void findrt(int u,int fa){
     36     sz[u]=1,son[u]=0;
     37     for(int i=head[u];i;i=Next[i]){
     38         int v=ver[i];
     39         if(!vis[v]&&v!=fa){
     40             findrt(v,u);
     41             sz[u]+=sz[v];
     42             cmax(son[u],sz[v]);
     43         }
     44     }
     45     cmax(son[u],size-sz[u]);
     46     if(son[u]<son[rt]) rt=u;
     47 }
     48 void dfs1(int u,int fa){
     49     //要重新dfs一次,不能直接在找根时的树上做(不然子树和之类的会出错) 
     50     //顺便维护各种东西 
     51     sz[u]=1,++cnt[c[u]];
     52     for(int i=head[u];i;i=Next[i]){
     53         int v=ver[i];
     54         if(!vis[v]&&v!=fa){
     55             dfs1(v,u),sz[u]+=sz[v];
     56         }
     57     }
     58     if(cnt[c[u]]==1) sum+=sz[u],col[c[u]]+=sz[u];
     59     --cnt[c[u]];
     60 }
     61 void change(int u,int fa,int val){
     62     ++cnt[c[u]];
     63     for(int i=head[u];i;i=Next[i]){
     64         int v=ver[i];
     65         if(!vis[v]&&v!=fa) change(v,u,val);
     66     }
     67     if(cnt[c[u]]==1) sum+=sz[u]*val,col[c[u]]+=sz[u]*val;
     68     --cnt[c[u]];
     69 }
     70 void dfs2(int u,int fa){
     71     //把这棵子树里的颜色的影响消除掉
     72     //顺便更新答案 
     73     ++cnt[c[u]];
     74     if(cnt[c[u]]==1) sum-=col[c[u]],++num;
     75     ans[u]+=sum+num*much;
     76     for(int i=head[u];i;i=Next[i]){
     77         int v=ver[i];
     78         if(!vis[v]&&v!=fa) dfs2(v,u);
     79     }
     80     if(cnt[c[u]]==1) sum+=col[c[u]],--num;
     81     --cnt[c[u]];
     82 }
     83 void clear(int u,int fa){
     84     cnt[c[u]]=col[c[u]]=0;
     85     for(int i=head[u];i;i=Next[i]){
     86         int v=ver[i];
     87         if(!vis[v]&&v!=fa) clear(v,u);
     88     }
     89 }
     90 void did(int u){
     91     //直接带进去乱搞 
     92     dfs1(u,0);
     93     ans[u]+=sum-col[c[u]]+sz[u];
     94     for(int i=head[u];i;i=Next[i]){
     95         int v=ver[i];
     96         if(!vis[v]){
     97             //dfs,然后把各种影响消除掉 
     98             ++cnt[c[u]];
     99             sum-=sz[v];
    100             col[c[u]]-=sz[v];
    101             change(v,u,-1);
    102             --cnt[c[u]];
    103             much=sz[u]-sz[v];
    104             dfs2(v,u);
    105             ++cnt[c[u]];
    106             sum+=sz[v];
    107             col[c[u]]+=sz[v];
    108             change(v,u,1);
    109             --cnt[c[u]];
    110         }
    111     }
    112     sum=0,num=0,clear(u,0);
    113 }
    114 void solve(int u){
    115     did(u),vis[u]=true;
    116     for(int i=head[u];i;i=Next[i]){
    117         int v=ver[i];
    118         if(!vis[v]){
    119             rt=0,size=sz[v];
    120             findrt(v,0),solve(rt);
    121         }
    122     }
    123 }
    124 int main(){
    125     n=read();
    126     for(int i=1;i<=n;++i) c[i]=read();
    127     for(int i=1;i<n;++i){
    128         int u=read(),v=read();
    129         add(u,v);
    130     }
    131     son[0]=n+1,rt=0,size=n;
    132     findrt(1,0),solve(rt);
    133     for(int i=1;i<=n;++i) print(ans[i]);
    134     Ot();
    135     return 0;
    136 }
  • 相关阅读:
    node下运行ts
    npm的一些基本配置设置
    windws 安装jdk
    java jdbc连接mysql
    struts2+jquery 实现ajax登陆
    struts2 零配置
    java 生成UUID
    ubuntu 换源
    ubuntu下安装redis
    安装 vsftpd
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9484115.html
Copyright © 2020-2023  润新知