浅谈树分治:https://www.cnblogs.com/AKMer/p/10014803.html

题目传送门:https://www.luogu.org/problemnew/show/P2664

对于所有求颜色种类数的问题,我们都可以定义一个方向,使得所有的颜色在最靠这个方向第一次出现的位置有效,而其它位置都是无效的。对于树分治,我们可以定义这个方向为当前需要遍历的子树,反方向就是已经遍历完的子树。

对于一个点\(u\),如果从当前重心到他这一条路径上,该点颜色是第一次出现,那么它的颜色将给后面的遍历带来\(siz[u]\)的贡献。另外,在遍历当前子树时,所有在重心到当前点这条路径的上的颜色,贡献都是已经遍历过的子树的总结点数。正过来做一遍,反过来做一遍就可以了。对于单独的从重心到当前点的路径会被统计两次,所以要减掉一次。

边分治重构树之后不知道怎么消除新结点的影响,如果有大佬愿意教教我请在评论下方回复。

这题数据貌似比较水,不卡不重构树的边分治。

时间复杂度:\(O(nlogn)\)

空间复杂度:\(O(n)\)

点分治版代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll; const int maxn=1e5+5; bool vis[maxn];
ll ans[maxn],res;
int n,tot,mx,rt,N,Siz;
int now[maxn],pre[maxn<<1],son[maxn<<1];
int col[maxn],siz[maxn],cnt[maxn],V[maxn],sum[maxn]; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} void add(int a,int b) {
pre[++tot]=now[a];
now[a]=tot,son[tot]=b;
} struct rubbish {
bool bo[maxn];
int sta[maxn],top; void clear() {
Siz=res=0;
while(top) {
bo[sta[top]]=0;
cnt[sta[top--]]=0;
}
} void ins(int id) {
if(bo[id])return;
bo[id]=1,sta[++top]=id;
}
}R; void find_rt(int fa,int u) {
int res=0;siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)find_rt(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
res=max(res,N-siz[u]);
if(res<mx)mx=res,rt=u;
} void dfs(int fa,int u) {
sum[col[u]]++,res+=(sum[col[u]]==1);
ans[u]-=res,siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)dfs(u,v),siz[u]+=siz[v];
sum[col[u]]--,res-=(sum[col[u]]==0);
} void query(int fa,int u) {
sum[col[u]]++;if(sum[col[u]]==1)res-=cnt[col[u]],res+=Siz+1;
ans[u]+=res;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)query(u,v);
sum[col[u]]--;if(sum[col[u]]==0)res+=cnt[col[u]],res-=Siz+1;
} void solve(int fa,int u) {
sum[col[u]]++;
if(sum[col[u]]==1) {
cnt[col[u]]+=siz[u];
res+=siz[u];R.ins(col[u]);
}
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)solve(u,v);
sum[col[u]]--;
} void work(int u,int size) {
N=size,mx=rt=n+1,find_rt(0,u);
u=rt,vis[u]=1,tot=0;
sum[col[u]]++;res++;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v])V[++tot]=v,dfs(u,v);
sum[col[u]]--;res--;
for(int i=1;i<=tot;i++) {
int v=V[i];
sum[col[u]]++,res-=cnt[col[u]],res+=Siz+1;
query(u,v);
sum[col[u]]--,res+=cnt[col[u]],res-=Siz+1;
solve(u,v),Siz+=siz[v];
}R.clear();
for(int i=tot;i;i--) {
int v=V[i];
sum[col[u]]++,res-=cnt[col[u]],res+=Siz+1;
query(u,v);
sum[col[u]]--,res+=cnt[col[u]],res-=Siz+1;
solve(u,v),Siz+=siz[v];
}ans[u]+=res+Siz+1-cnt[col[u]];R.clear();
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v])work(v,siz[v]);
} int main() {
n=read();
for(int i=1;i<=n;i++)
col[i]=read();
for(int i=1;i<n;i++) {
int a=read(),b=read();
add(a,b),add(b,a);
}work(1,n);
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]);
return 0;
}

不重构树的边分治版代码如下:

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll; const int maxn=2e5+5; bool vis[maxn];
ll ans[maxn],res;
int m,n,tot=1,mx,id,N;
int now[maxn],pre[maxn<<1],son[maxn<<1];
int col[maxn],siz[maxn],cnt[maxn],sum[maxn]; vector<int>to[maxn];
vector<int>::iterator it; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} void add(int a,int b) {
pre[++tot]=now[a];
now[a]=tot,son[tot]=b;
} struct Rubbish {
bool bo[maxn];
int sta[maxn],top; void clear() {
res=0;
while(top)cnt[sta[top]]=bo[sta[top]]=0,top--;
} void ins(int id) {
if(bo[id])return;
bo[id]=1,sta[++top]=id;
}
}R; void find_edge(int fa,int u) {
siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa) {
find_edge(u,v),siz[u]+=siz[v];
if(abs(N-2*siz[v])<mx)
mx=abs(N-2*siz[v]),id=p>>1;
}
} void dfs(int fa,int u) {
siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)dfs(u,v),siz[u]+=siz[v];
} void solve(int fa,int u) {
sum[col[u]]++;
if(sum[col[u]]==1) {
cnt[col[u]]+=siz[u];
res+=siz[u],R.ins(col[u]);
}
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)solve(u,v);
sum[col[u]]--;
} void query(int fa,int u,int num) {
sum[col[u]]++;
if(sum[col[u]]==1)res+=num,res-=cnt[col[u]];
ans[u]+=res;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)query(u,v,num);
sum[col[u]]--;
if(sum[col[u]]==0)res-=num,res+=cnt[col[u]];
} void work(int u,int size) {
if(size<2)return;
N=size,mx=id=m+1,find_edge(0,u),vis[id]=1;
int u1=son[id<<1],u2=son[id<<1|1];
dfs(0,u1),dfs(0,u2);
solve(0,u1),query(0,u2,siz[u1]),R.clear();
solve(0,u2),query(0,u1,siz[u2]),R.clear();
work(u1,siz[u1]),work(u2,siz[u2]);
} int main() {
m=n=read();
for(int i=1;i<=n;i++)
col[i]=read();
for(int i=1;i<n;i++) {
int a=read(),b=read();
add(a,b),add(b,a);
}
work(1,m);
for(int i=1;i<=n;i++)printf("%lld\n",ans[i]+1);
return 0;
}

洛谷【P2664】树上游戏的更多相关文章

  1. 洛谷 P2664 树上游戏 解题报告

    P2664 树上游戏 题目描述 \(\text{lrb}\)有一棵树,树的每个节点有个颜色.给一个长度为\(n\)的颜色序列,定义\(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量.以及 ...

  2. ●洛谷P2664 树上游戏

    题链: https://www.luogu.org/problemnew/show/P2664题解: 扫描线,线段树维护区间覆盖 https://www.luogu.org/blog/ZJ75211/ ...

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

    传送门 题解 因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直 ...

  4. 洛谷P2664 树上游戏

    https://www.luogu.org/problemnew/show/P2664 #include<cstdio> #include<algorithm> #includ ...

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

    题意 题目链接 Sol 神仙题..Orz yyb 考虑点分治,那么每次我们只需要统计以当前点为\(LCA\)的点对之间的贡献以及\(LCA\)到所有点的贡献. 一个很神仙的思路是,对于任意两个点对的路 ...

  6. 【刷题】洛谷 P2664 树上游戏

    题目描述 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 \[sum_i=\sum_{j=1}^ns(i,j)\] 现在他想让你求出所有 ...

  7. 洛谷P2664 树上游戏 【点分治 + 差分】

    题目 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 现在他想让你求出所有的sum[i] 输入格式 第一行为一个整数n,表示树节点的数量 ...

  8. 洛谷P2664 树上游戏——点分治

    原题链接 被点分治虐的心态爆炸了 题解 发现直接统计路径上的颜色数量很难,考虑转化一下统计方式.对于某一种颜色\(c\),它对一个点的贡献为从这个点出发且包含这种颜色的路径条数. 于是我们先点分一下, ...

  9. [洛谷U40581]树上统计treecnt

    [洛谷U40581]树上统计treecnt 题目大意: 给定一棵\(n(n\le10^5)\)个点的树. 定义\(Tree[l,r]\)表示为了使得\(l\sim r\)号点两两连通,最少需要选择的边 ...

  10. P2664 树上游戏

    P2664 树上游戏 https://www.luogu.org/problemnew/show/P2664 分析: 点分治. 首先关于答案的统计转化成计算每个颜色的贡献. 1.计算从根出发的路径的答 ...

随机推荐

  1. 想全面理解JWT?一文足矣!

    有篇关于JWT的文章,叫"JWT: The Complete Guide to JSON Web Tokens",写得全面细致.为了自己能更清晰理解并惠及更多人,我把它大致翻译了过 ...

  2. anaconda2下面安装opencv2.4.13.4完成----解决默认安装的问题----Thefunction is not implemented. Rebuild the library with Windows, GTK+ 2.x orCarbon support. If you are on Ubuntu or Debian, install libgtk2.0‑dev and pkg

    转载自:http://blog.csdn.net/qingyanyichen/article/details/73550924 本人下载编译安装了opencv2.4.9,oppencv2.4.10,o ...

  3. python升级安装后的yum的修复

    升级python版本号后,执行yum # yum -y install openssl 提演示样例如以下: There was a problem importing one of the Pytho ...

  4. 【Linux】 awk应用

    1  统计机器上处于不同状态的所有TCP连接的个数(TCP连接是有状态连接,包含SYN_RECV, ESTABLISHED, TIME_WAIT, FIN_WAIT0, FIN_WAIT1等多种状态, ...

  5. C市现在要转移一批罪犯到D市,C市有n名罪犯,按照入狱时间有顺序,另外每个罪犯有一个罪行值,值越大罪越重。现在为了方便管理,市长决定转移入狱时间连续的c名犯人,同时要求转移犯人的罪行值之和不超过t,问有多少种选择的方式?

    // ConsoleApplication12.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" // ConsoleApplication1 ...

  6. Net dll版本兼容问题

    Net dll组件版本兼容问题 https://www.cnblogs.com/newP/p/9543528.html dll组件版本兼容问题,是生产开发中经常遇到的问题,常见组件兼容问题如:Newt ...

  7. javascript的defer和async(转载)

    http://ued.ctrip.com/blog/?p=3121 我们常用的javascript标签,有两个和性能.js文件下载执行相关的属性:defer和async defer的含义[摘自http ...

  8. wamp环境配置;转自发瑞的博客(www.cnblogs.com/cyrfr/p/6483529.html)

    php手动搭建环境有好多种组合,版本号不一致,会导致搭建失败. 我搭建的组合是: php5.6+MySQL5.6+Apache2.4的组合. 一.PHP语言包下载 首先从官网上下载php5.6 htt ...

  9. 【BZOJ2229】[Zjoi2011]最小割 最小割树

    [BZOJ2229][Zjoi2011]最小割 Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有 ...

  10. mysq'l系列之10.mysql优化&权限控制

    网站打开慢如何排查 1.打开网页, 用谷歌浏览器F12, 查看network: 哪个加载时间长就优化哪个 2.如果是数据库问题 2.1 查看大体情况 # top # uptime  //load av ...