edu round #2 E. Lomsat gelral

给出一棵以 \(1\) 为根的有 \(n\) 个节点的树,节点 \(i\) 的颜色为 \(c_i\) .

如果一种颜色 \(c\) 在子树 \(i\) 中出现次数最多,则称 \(c\) 在子树 \(i\) 中占有主导地位 .

节点 \(1\)\(n\) ,对于每个节点求出占有主导地位的颜色编号和 .

\(1\leq n\leq 10^5,1\leq c_i\leq n,1\leq x_i,y_i\leq n\)

看到树,想到线段树上区间和启发式合并.

但是,本题以节点为叶子,如果对于一个区间维护颜色最大出现次数和主导地位颜色编号和,是无法 \(O(1)\) 地计算的 .

此时,想到可以考虑启发式合并,按照颜色为叶子建立线段树,维护与一个区间最大出现次数和主导地位颜色编号和 ,这样就容易 \(O(1)\) 地计算了.

那么,如何合并两个线段树,emmm,可能跟线段树合并有关,但是我不会啊……

这就不可以做了吗?怎么可能 . 联想到轻重链剖分,其中必定是 size 小的子树向 size 最大的子树合并. 所以,只有 size 最大的子树建出的线段树在当前计算中是有用的,其他的树根本不用递归下去,是一个独立的子问题.

此时,可以考虑用一个 queue 维护需要处理的子问题,然后,dfs 之前提前计算出线段树的大小 ,再 dfs 递归,启发式合并,递归 size 最大的点,其余小的点为子问题,放入 queue 中,size 小的子树中的点一个一个加入线段树 . 最后,取出线段树的 \(1\) 号节点的主导地位颜色编号和,就是答案了. 要注意标号和要用 long long 存. 我估计只有我一个人这样写的这么奇葩.

时间复杂度 : \(O(nlog^2n)\)

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

code

#include<bits/stdc++.h>
using namespace std;
int n;
int c[100010];
vector<int>g[100010];
int L[100010],R[100010],id[100010],pos[100010],cnt=0;
void get_id(int x,int fa){
	id[x]=cnt++;
	pos[id[x]]=x;
	L[x]=R[x]=id[x];
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i];
		if(to==fa)continue;
		get_id(to,x);
		L[x]=min(L[x],L[to]);
		R[x]=max(R[x],R[to]);
	}
}
int sz[100010],par[100010];
void get_sz(int x,int fa){
	par[x]=fa;sz[x]=1;
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i];
		if(to==fa)continue;
		get_sz(to,x);
		sz[x]+=sz[to];
	}
}
bool heavy[100010];
class node{public:int val;long long sum;};
unordered_map<int,int>Map;
node tree[100010*4];
queue<int>Q;
void up(node &x,node l,node r){
	if(l.val!=r.val){
		if(l.val>r.val)x=l;
		else x=r;
	}
	else{
		x.val=l.val;
		x.sum=l.sum+r.sum;
	}
}
void init(int x,int l,int r,int pos,int val){
	if(l==r){
		tree[x]=(node){0,val};
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)init(x<<1,l,mid,pos,val);
	else init(x<<1|1,mid+1,r,pos,val);
	up(tree[x],tree[x<<1],tree[x<<1|1]);
}
void upd(int x,int l,int r,int pos,int val){
	if(l==r){
		tree[x].val++;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)upd(x<<1,l,mid,pos,val);
	else upd(x<<1|1,mid+1,r,pos,val);
	up(tree[x],tree[x<<1],tree[x<<1|1]);
}
long long ans[100010];
void dfs(int x,int fa){
	int Id=-1;
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i];
		if(to==fa)continue;
		if(Id==-1||sz[Id]<sz[to])Id=to;
	}
	if(Id!=-1){
		dfs(Id,x);
	}
	upd(1,0,cnt-1,Map[c[x]],1);
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i];
		if(to==fa||to==Id)continue;
		for(int j=L[to];j<=R[to];j++){
			int y=pos[j];
			upd(1,0,cnt-1,Map[c[y]],1);
		}
		Q.push(to);
	}
	ans[x]=tree[1].sum;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=0;i<n;i++)cin>>c[i];
	for(int i=0;i<n-1;i++){
		int u,v;
		cin>>u>>v;
		u--;v--;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	get_id(0,-1);
	get_sz(0,-1);
	Q.push(0);
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		cnt=0;Map.clear();
		for(int i=L[x];i<=R[x];i++){
			int y=pos[i];
			if(Map.find(c[y])==Map.end())Map[c[y]]=cnt++;
		}
		for(unordered_map<int,int>::iterator it=Map.begin();it!=Map.end();it++)
			init(1,0,cnt-1,it->second,it->first);
		dfs(x,par[x]);
	}
	for(int i=0;i<n;i++)cout<<ans[i]<<" ";
	cout<<"\n";
	return 0;
}

edu round #2 E. Lomsat gelral

上一篇:ubuntu nvidia显卡切换


下一篇:centos7 防火墙使用iptables