dsu on tree 专题




void dfs(int u,int fa)
{
	d[u]=d[fa]+1;
	sz[u]=1;
	int ma=-1;
	for(int v:g[u])
	{
		if(v==fa)continue;
		dfs(v,u);
		sz[u]+=sz[v];
		if(sz[v]>ma)
		{
			ma=sz[v];
			son[u]=v;
		}
	}
}

给定一个以1为根的n个节点的树,每个点上有一个字母(a-z),
每个点的深度定义为该节点到1号节点路径上的点数.
每次询问 a,b.查询以a为根的子树内深度为b的节点上的字母重新排列之后是否能构成回文串.

void cal(int u,int fa,int f)
{
cnt[d[u]][col[u]]+=f;
for(int v:g[u])
{
	if(v==fa|v==now)continue;
	cal(v,u,f);
}
}
void dfs(int u,int fa,int f)
{
	for(int v:g[u])
	{
		if(v==fa||v==son[u])continue;
		dfs(v,u,0);
	}
	if(son[u])dfs(son[u],u,1)now=son[u];
	cal(u,fa,1);
	for(int i=0;i<q[u].size();i++)
	{
		int d=q[u][i].y;
		int id=q[u][i].x;
		int num=0;
		for(int i=1;i<=26;i++)
		{
			if(cnt[d][i]&1)num++;
		}
		ans[id]=num>1?0:-1;
		
	}
	now=0;
	if(!f)cal(u,fa,-1);
}

给你一片森林,每次询问一个点与多少个点拥有共同的K级祖先
问题转化为 他的k级祖先上有多少个深度相同的儿子
我们先离线询问

void dfs(int u,int fa)
{
   d[u]=d[fa]+1;
   f[u][0]=fa;
   for(int i=1;i<=20;i++)
   {
    fa[u][i]=fa[fa[u][i-1]][i-1];	
   }
   for(int v:g[u])
   {
   	if(v==fa)continue;
   	dfs(v,u);
   }
	
}
int cal(int u,int fa,int f)
{
	cnt[d[u]]+=1;
	for(int v:g[u])
	{
		if(v==fa||v==now)continue;
		cal(v,u,f);
	}
}
void dfs(int u,int fa,int f)
{
	for(int v:g[u])
	{
		if(v==fa||v==son[u])continue;
		dfs(v,u,0);
	}
	if(son[u]){
		dfs(son[u],u,1);
		now=son[u];
	}
	cal(u,fa,1);
	for(int i=0;i<q[u].size();i++)
	{
		int id=q[u][i].x;
		int  d=q[u][i].y;
		ans[id]=cnt[d]-1;
	}
	now=0;
	if(!f)cal(u,fa,-1);
}

   for(int j=20;j>=0;j--)
   {
   	if(p&(1<<j))
   	u=fa[u][j];
   }
   q[u].pb({i,d});
   
   

给定一片森林,每次询问一个节点的K-Son共有个多少不同的名字。一个节点的K-Son即为深度是该节点深度加K的节点。
map维护

 map<string,int>a[N];

 void cal(int u,int fa,int f)
  {
  	if(!a[d[u]][s[u]]) cnt[d[u]]+=f;
  	a[d[u]][s[u]]+=f;
  	if(!a[d[u]][s[u]]) cnt[d[u]]-=f;
    for(int v:g[u])
	{
     if(v==fa||v==now)continue;
	 cal(v,u,f);		
    }  	
  }
  
  
  
  for(int i=0;i<q[u].size();i++)
  {
  	int id=q[u][i].x;
  	int d=q[u][i].y;
  	ans[id]=cnt[d];
  }
  
  for(int i=1;i<=m;i++)
  {
  	int x,y;
  	q[x].pb({i,d[x]+y});
  }
   

给定一棵以 1 为根,n 个节点的树。设 d(u,x)为 u 子树中到 u 距离为 x 的节>点数。
对于每个点,求一个最小的 k,使得 d(u,k) 最大。

void cal(int u,int fa,int f)
{
	cnt[d[u]]+=f;
	if(f>0&&cntd[d[u]]>=mac)
	{
		if(cnt[d[u]]>mac){
			mac=cnt[d[u]];
			mi=d[u];
		}
		else if(cnt[d[u]]==mac&&d[u]<mi){
			mi=d[u];
		}
	}
	for(int v:g[u])
	{
		if(v==fa||v==now)continue;
		cal(v,u,f);
	}
}


void dfs(int u,int fa,int f)
{
	for(int v:g[u])
	{
		if(v==fa||v==son[u])continue;
		dfs(v,u,0);
	}
	if(son[u]){
		dfs(son[u],u,1);
		now=son[u];
	}
	cal(u,fa,1);
	ans[u]=mi-d[u];
	 now=0;
	 if(!f)cal(u,f,-1),mi=inf,mxc=0;
}
   

给出一棵 n 个结点的树,每个结点有一个颜色 c i 。
询问 q 次,每次询问以 v 结点为根的子树中,出现次数 ≥k 的颜色有多少种。树的根节点是1。
cnt[N] 表示出现次数大于等于k的种类数

void cal(int u,int fa,int f)
{
if(f=-1)num[cnt[col[u]]]+=f;
cnt[col[u]]+=f;
if(f==1)num[cnt[col[u]]]+=f;
for(int v:g[u])
{
	if(v==fa||v==now)continue;
	dfs(v,u,f);
}
	
}

for(int i=0;i<q[u].size();i++)
{
	int id=q[u][i].x;
	int k=q[u][i].y;
	ans[id]=num[k];
}
   

给你一个n个节点的树,求每个节点的"结实程度"
一个节点的结实程度定义为以该节点为根的子树里所有节点的编号从小到大>排列后,相邻编号的平方和。

void calc(int u, int f, int k)
{
    if (k == 1)
    {
        if (s.empty())
            s.insert(u);
        else
        {
            if (s.lower_bound(u) == s.end())
            {
                set<int>::iterator it = s.end();
                it--;
                sum += 1ll*(u - *it) * (u - *it);
            }
            else if (s.lower_bound(u) == s.begin())
            {
                set<int>::iterator it = s.begin();
                sum += 1ll*(*it - u) * (*it - u);
            }
            else
            {
                set<int>::iterator it = s.lower_bound(u);
                set<int>::iterator it1 = it;
                it1--;
                sum -= 1ll*(*it - *it1) * (*it - *it1);
                sum += 1ll*(u - *it1) * (u - *it1);
                sum += 1ll*(*it - u) * (*it - u);
            }
            s.insert(u);
        }
    }
    else
        s.erase(u);
    for (auto v : G[u])
        if (v!=now && v != f)
            calc(v, u, k);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值