标签:Colorful int Tree son dep ans 我们 dis
题目大意
给我们一棵树,每个节点有一个颜色。
需要完成两个操作。
- 求某一种颜色的最小生成树的边数
- 将某一个节点的颜色改为另一种颜色
思路
不得不提到一个名词,虚树。虚树的概念大就是说我们不用真的把每一次改变后的树都重新建出来,我们只需要维护某些性质即可。
大部分用到虚树的题目都是树上节点有颜色,然后对某一种颜色的所有节点进行操作。
所以,本题我们自然想到,我们只对一种颜色维护其生成树可以嘛?
当然是可以的,这是个经典的算法了。
求树中x个点的生成树的权值和
静态计算是动态计算的其中一种情况,我们直接说动态计算。
首先,我们将树中所有点跑出dfs序
。
我们计算贡献时用的是同一个式子。
假设,我们求得是z的贡献,我们取的点时x,y。其满足dfn[z]<dfn[z]<dfn[y]
我们算出来的是z的贡献的二倍
插入一个新点时,我们分两种情况。
- 第一种情况,插入的点在当前已经插入的点的边界处,那我们直接取已经插入的任意两点(可以取同一个)。为了方便,我们直接去当前的两个边界。
- 第二种情况,插入的点的两个边界可以找到,则直接代入式子。
如果是删除一个点,则反向减去贡献。
我们来看看代码。
我们以树中有多种颜色,即,同时维护多个求生成树权值和为例。
struct Scmp//按dfs序从小到大排序。
{
bool operator()(int x,int y) const
{
return dfn[x]<dfn[y];
}
};
set<int,Scmp> s[N];
int dis(int u,int v)
{
return dep[u] + dep[v] - 2*dep[lca(u,v)];
}
void update(int x,int f)//x代表的是树中的节点,f代表的操作类型
{
if(f==-1) s[c[x]].erase(x);//-1则将x从原本所待的集合中删除
auto it = s[c[x]].lower_bound(x);//找到x的在集合中边界
if(s[c[x]].empty()) ans[c[x]] = 0;//若此时为空,则对应集合的值为0
else if(it==s[c[x]].begin()||it==s[c[x]].end())//若x此时位于边界位置,则我们取的为集合中边界的两点(最大最小)
{
ans[c[x]] += f*dis(*s[c[x]].begin(),x);
ans[c[x]] += f*dis(*s[c[x]].rbegin(),x);
ans[c[x]] -= f*dis(*s[c[x]].begin(),*s[c[x]].rbegin());
}
else //否则,我们就找x附近的两点
{
auto nt = it,pre = --it;
ans[c[x]] += f*dis(*nt,x);
ans[c[x]] += f*dis(*pre,x);
ans[c[x]] -= f*dis(*pre,*nt);
}
if(f==1) s[c[x]].insert(x);//1,则将x计入新的集合
}
知道了,这个,那就结束了嘞。
看代码
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int h[N],e[N<<1],ne[N<<1],idx;
int dep[N],sz[N],fa[N],son[N];
int dfn[N],top[N],ts;
int ans[N],c[N];
int n,m;
struct Scmp//按dfs序从小到大排序。
{
bool operator()(int x,int y) const
{
return dfn[x]<dfn[y];
}
};
set<int,Scmp> s[N];
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa)
{
sz[u] = 1;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
fa[j] = u;dep[j] = dep[u] + 1;
dfs1(j,u);
sz[u] += sz[j];
if(sz[j]>sz[son[u]]) son[u] = j;
}
}
void dfs2(int u,int tp)
{
dfn[u] = ++ts,top[u] = tp;
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa[u]||j==son[u]) continue;
dfs2(j,j);
}
}
int lca(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u = fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int dis(int u,int v)
{
return dep[u] + dep[v] - 2*dep[lca(u,v)];
}
void update(int x,int f)
{
if(f==-1) s[c[x]].erase(x);
auto it = s[c[x]].lower_bound(x);
if(s[c[x]].empty()) ans[c[x]] = 0;
else if(it==s[c[x]].begin()||it==s[c[x]].end())
{
ans[c[x]] += f*dis(*s[c[x]].begin(),x);
ans[c[x]] += f*dis(*s[c[x]].rbegin(),x);
ans[c[x]] -= f*dis(*s[c[x]].begin(),*s[c[x]].rbegin());
}
else
{
auto nt = it,pre = --it;
ans[c[x]] += f*dis(*nt,x);
ans[c[x]] += f*dis(*pre,x);
ans[c[x]] -= f*dis(*pre,*nt);
}
if(f==1) s[c[x]].insert(x);
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=1;i<n;i++)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs1(1,-1),dfs2(1,1);
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
update(i,1);
}
scanf("%d",&m);
while(m--)
{
char op[5];
scanf("%s",op);
if(*op=='Q')
{
int x;scanf("%d",&x);
printf("%d\n",s[x].empty()?-1:ans[x]/2);
}
else
{
int x,y;scanf("%d%d",&x,&y);
update(x,-1);
c[x] = y;
update(x,1);
}
}
return 0;
}
标签:Colorful,int,Tree,son,dep,ans,我们,dis 来源: https://www.cnblogs.com/aitejiu/p/16429048.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。