CF1009F Dominant Indices (树上启发式合并维护线段树+常数优化)

传送门

给定有根树,定义d[x,i]表示以x为根的树中,与x的距离为i的节点个数,对于每棵子树,求出使得d[x,i]最大的i,存在多个输出最小的。

其实是一道dsu on tree的模板题了…
正解:cnt[i]表示当前子树深度为i的节点个数,每次更新都是用的当前子树的节点进行更新,所以可以直接用全局变量维护最大值,然后更新就行。

但是这题卡常,刚开始add函数是用递归写的,就t了,改成dfs序很快就跑完了。

递归:
在这里插入图片描述
dfs序:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e6 + 10;
const ll mod = 1e9+7;
const ll inf = (ll)4e17;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
//给定有根树 定义d(x,i)为x的后代中 深度比x多i的节点个数 对于每个x  求d数组的最大值下标
//用全局变量维护d数组的最大值
int n,m;
vector<int> g[maxn];
int dep[maxn],son[maxn],size[maxn],in[maxn],clk,pos[maxn];
int ans[maxn];
int idx=maxn,mmax=-1;
int d[maxn];//深度为i 个数
void dfs(int rt,int fa)
{
    
    	
	dep[rt]=dep[fa]+1;
	in[rt]=++clk;
	pos[clk]=rt;
	size[rt]=1;
	for(int i:g[rt]) 
	{
    
    
		if(i==fa) continue;
		dfs(i,rt);
		size[rt]+=size[i];
		if(size[i] > size[son[rt]]) son[rt]=i;
	}
}
int SON;
void add(int rt,int v) 
{
    
    
	for(int i=in[rt];i<in[rt]+size[rt];i++) 
	{
    
    
		//pos[i] 就是节点
		int u=pos[i];
		if(u==SON) 
		{
    
    
			i=i+size[SON]-1;//跳过SON的整棵子树
			continue;
		}
		d[dep[u]]+=v;
		if(d[dep[u]]>mmax || (d[dep[u]]==mmax && dep[u]<idx)) 
		{
    
    
			mmax=d[dep[u]];
			idx=dep[u];
		}
	}
}
void dfs2(int rt,int fa,bool ok) 
{
    
    
	for(int i:g[rt]) 
	{
    
    
		if(i==fa || i==son[rt]) continue;
		dfs2(i,rt,0);
	}
	if(son[rt]) dfs2(son[rt],rt,1),SON=son[rt];
	add(rt,1),SON=0;
	ans[rt]=idx-dep[rt];
	if(!ok) add(rt,-1),mmax=-1,idx=maxn;//删除后 全局维护的变量也要记得清空
}
int main()
{
    
    
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++) 
	{
    
    
		scanf("%d %d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1,0);
	dfs2(1,0,0);
	for(int i=1;i<=n;i++)
	{
    
    
		printf("%d\n",ans[i]);
	}
	return 0;
}

题外话:
or这题的第一反应想复杂了,觉得对于子树rt是在cnt数组[dep[rt],n]中取最大值,想到用线段树维护,单点修改区间查询。O(nlognlogn)
不过用线段树写完居然可以过…(当然add函数还是用的dfs序,用递归也t了)
在这里插入图片描述

加上线段树的版本:

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e6 + 10;
const ll mod = 1e9+7;
const ll inf = (ll)4e17;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
//给定有根树 定义d(x,i)为x的后代中 深度比x多i的节点个数 对于每个x  求d数组的最大值下标
//dsu on tree  线段树维护d[] 深度为i的点的个数 每次查询[dep[rt],n]最大值
 
//nlognlogn
 
pii tree[maxn<<2];//fi 个数 se 深度
inline void pushup(int rt) 
{
    
    
	if(tree[lc].first != tree[rc].first)
		tree[rt]=max(tree[lc],tree[rc]);
	else 
	{
    
    
		if(tree[lc].second < tree[rc].second)  tree[rt]=tree[lc];
		else tree[rt]=tree[rc];
	} 
}
inline void build(int rt,int l,int r)
{
    
    
	if(l==r) 
	{
    
    
		tree[rt].first=0;
		tree[rt].second=l;
		return ;
	}
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	pushup(rt);
}
inline void upd(int rt,int l,int r,int pos,int v) 
{
    
    
	if(l==r)
	{
    
    
		tree[rt].first+=v;
		return ;
	}
	int mid=l+r>>1;
	if(pos<=mid) upd(lc,l,mid,pos,v);
	else upd(rc,mid+1,r,pos,v);
	pushup(rt);
}
inline pii qry(int rt,int l,int r,int vl,int vr) 
{
    
    	
	if(vl<=l && r<=vr) return tree[rt];
	int mid=l+r>>1;
	if(vr<=mid) return qry(lc,l,mid,vl,vr);
	else if(vl>mid) return qry(rc,mid+1,r,vl,vr);
	pii t1=qry(lc,l,mid,vl,vr),t2=qry(rc,mid+1,r,vl,vr);
	if(t1.first != t2.first) return max(t1,t2);
	else 
	{
    
    
		if(t1.second < t2.second) return t1;
		else return t2;
	}
}
int n,m;
vector<int> g[maxn];
int dep[maxn],son[maxn],size[maxn],in[maxn],clk,pos[maxn];
int ans[maxn];
void dfs(int rt,int fa)
{
    
    	
	dep[rt]=dep[fa]+1;
	in[rt]=++clk;
	pos[clk]=rt;
	size[rt]=1;
	for(int i:g[rt]) 
	{
    
    
		if(i==fa) continue;
		dfs(i,rt);
		size[rt]+=size[i];
		if(size[i] > size[son[rt]]) son[rt]=i;
	}
}
int SON;
void add(int rt,int v) 
{
    
    
	for(int i=in[rt];i<in[rt]+size[rt];i++) 
	{
    
    
		int u=pos[i];
		if(u==SON) 
		{
    
    
			i=i+size[SON]-1;
			continue;
		}
		upd(1,1,n,dep[u],v);
	}
/*	upd(1,1,n,dep[rt],v);
	for(int i:g[rt]) 
	{
		if(i==fa || i==SON) continue;
		add(i,rt,v);
	}
*/
}
void dfs2(int rt,int fa,bool ok) 
{
    
    
	for(int i:g[rt]) 
	{
    
    
		if(i==fa || i==son[rt]) continue;
		dfs2(i,rt,0);
	}
	if(son[rt]) dfs2(son[rt],rt,1),SON=son[rt];
	add(rt,1),SON=0;
	ans[rt]=qry(1,1,n,dep[rt],n).second-dep[rt];
	if(!ok) add(rt,-1);
}
int main()
{
    
    
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++) 
	{
    
    
		scanf("%d %d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	build(1,1,n);
	dfs(1,0);
	dfs2(1,0,0);
	for(int i=1;i<=n;i++)
	 	printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/120595311