洛谷【P2664】树上游戏
浅谈树分治: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】树上游戏的更多相关文章
- 洛谷 P2664 树上游戏 解题报告
P2664 树上游戏 题目描述 \(\text{lrb}\)有一棵树,树的每个节点有个颜色.给一个长度为\(n\)的颜色序列,定义\(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量.以及 ...
- ●洛谷P2664 树上游戏
题链: https://www.luogu.org/problemnew/show/P2664题解: 扫描线,线段树维护区间覆盖 https://www.luogu.org/blog/ZJ75211/ ...
- 洛谷P2664 树上游戏(点分治)
传送门 题解 因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直 ...
- 洛谷P2664 树上游戏
https://www.luogu.org/problemnew/show/P2664 #include<cstdio> #include<algorithm> #includ ...
- 洛谷P2664 树上游戏(点分治)
题意 题目链接 Sol 神仙题..Orz yyb 考虑点分治,那么每次我们只需要统计以当前点为\(LCA\)的点对之间的贡献以及\(LCA\)到所有点的贡献. 一个很神仙的思路是,对于任意两个点对的路 ...
- 【刷题】洛谷 P2664 树上游戏
题目描述 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 \[sum_i=\sum_{j=1}^ns(i,j)\] 现在他想让你求出所有 ...
- 洛谷P2664 树上游戏 【点分治 + 差分】
题目 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 现在他想让你求出所有的sum[i] 输入格式 第一行为一个整数n,表示树节点的数量 ...
- 洛谷P2664 树上游戏——点分治
原题链接 被点分治虐的心态爆炸了 题解 发现直接统计路径上的颜色数量很难,考虑转化一下统计方式.对于某一种颜色\(c\),它对一个点的贡献为从这个点出发且包含这种颜色的路径条数. 于是我们先点分一下, ...
- [洛谷U40581]树上统计treecnt
[洛谷U40581]树上统计treecnt 题目大意: 给定一棵\(n(n\le10^5)\)个点的树. 定义\(Tree[l,r]\)表示为了使得\(l\sim r\)号点两两连通,最少需要选择的边 ...
- P2664 树上游戏
P2664 树上游戏 https://www.luogu.org/problemnew/show/P2664 分析: 点分治. 首先关于答案的统计转化成计算每个颜色的贡献. 1.计算从根出发的路径的答 ...
随机推荐
- Django之tag的使用
settings.py: #安装 pip install django-taggit INSTALLED_APPS = [ 'myblog', 'taggit', 'django.contrib.ad ...
- SQL Prompt 编辑
SQL Prompt是一款拥有SQL智能提示功能的SQL Server和VS插件.超级好用的插件,
- 如何利用Require.Js管理多页面站点文件(译)
英文版地址 最近使用 Require.Js 的时候我发现它确实是一个改善代码管理的一个好方法.我以前发表Backbone类的文章时曾提到过 Require,但此前,我从未在传统的多页面网站内使用到 R ...
- 在另一个线程中无法用((CMainFrame *)AfxGetMainWnd())
一个vc6的项目放到vc8下重新编译这里死活过不去 查了些资料无果后来翻到一句老外的回答 If AfxGetMainWnd is called from the application’s prima ...
- GS与网络打交道
与网络打交道 在GS,GC,Share都与网络打交道,但还是GC最多 GC打交道过程 send_stat BaseChannel::SendCmdTry() { if (!m_queCmd.size( ...
- <转载> pycharm快捷键及一些常用设置
1.编辑(Editing ) Ctrl + Space 基本的代码完成(类.方法.属性)Ctrl + Alt + Space 快速导入任意类Ctrl + Shift + Enter 语句完成Ctrl ...
- EasyPlayer播放海康大华RTSP流时RTSPClient客户端连接兼容问题的解决
在之前的博客<EasyPlayer RTSP播放器对RTSP播放地址url的通用兼容修改意见>中,我描述了遇到的一个客户在播放大华某款摄像机时地址不兼容的问题,这不,团队刚刚参考我的这个意 ...
- S2S4H整合注意问题
整合过程中出现问题记录: 1.The import javax.servlet.http.HttpServletRequest cannot be resolved 解决办法:在tomcat的lib目 ...
- 九度OJ 1039:Zero-complexity Transposition(逆置) (基础题)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3093 解决:1255 题目描述: You are given a sequence of integer numbers. Zero-co ...
- Linux C语言 网络编程(二) server模型
前面介绍了关于连接linux服务端方式,可是服务端的资源是有限的,所以我们通常须要又一次思考,设计一套server模型来处理相应的client的请求. 第一种:并发server.通过主进程统一处理cl ...