给出一颗含有n个结点(全为白点)的含有边权(可为负)的树,询问q次,有两种操作类型:
C x :将结点x反色(白->黑 or 黑->白)
A :询问图中路径边权和最大的两个白点的路径边权和(此后用“距离”代替)
注意不要把线段树和原树和在一起理解
C x 操作使问题动态,需要维护,增加复杂度。
A操作没有固定询问关于某一个结点的距离,可能要维护整张图的最大距离才可以一次出解。
树剖+压缩重链到点上+递归更新
特点:重链压缩
Dfs :更新树的属性
fa:父亲的点编号,fav:父亲到当前节点的距离
tid:dfn的值,rnk:tid的反函数(tid[x]=y,rnk[y]=x)
top:重链的顶端的编号,son:重儿子的编号,presum:重链从top到当前点的前缀和。
注意不能把编号、dfn和线段树id弄混了
void Dfs1(int x,int fax) {
fa[x]=fax;
sz[x]=1;
son[x]=-1;
for(int i=0;iint v=G[x][i];
if(v!=fax) {
Dfs1(v,x);
sz[x]+=sz[v];
if(son[x]==-1||sz[son[x]]else fav[x]=W[x][i];
}
}
void Dfs2(int x,int tp) {
top[x]=tp;
tid[x]=++dcnt;
sz[tp]++;
if(x!=tp)
presum[tid[x]]=presum[tid[fa[x]]]+fav[x];//前缀和
rnk[dcnt]=x;
if(son[x]==-1) return ;
Dfs2(son[x],tp);
for(int i=0;iint v=G[x][i];
if(v!=son[x]&&v!=fa[x])
Dfs2(v,v);
}
}
更新节点的值(如图):使用multiset存储multiset与set的区别
void Build(int l,int r,int id,bool flag) {//当flag为1表示当前为top结点
if(l==r) {//叶节点
int u=rnk[l];
for(int i=0;i.size();i++) {
int v=G[u][i];
if(v!=fa[u]&&top[u]!=top[v]) {
root[v]=++cnt;//用root记录top对应的线段树的id
Build(tid[v],tid[v]+sz[v]-1,root[v],1);//对轻边子树建线段树
ch[u].insert(tree[root[v]].maxl+W[u][i]);//更新u点的值
}
}
Updata(u,id);//对u单点更新,详见下一段代码
if(flag==1)//注意这里只更新top结点,其他结点不更
ansx.insert(tree[id].v);
}
else {//非叶节点
int mid=(l+r)>>1;
tree[id].pl=++cnt;
tree[id].pr=++cnt;//pl,pr类型为id
Build(l,mid,tree[id].pl,0);
Build(mid+1,r,tree[id].pr,0);//id线段树类型分两段处理
Merge(id,tree[id].pl,tree[id].pr,l,r);//PushUp,利用儿子更新
if(flag==1)
ansx.insert(tree[id].v);
}
}
Build的更新方式:Merge(非叶节点通过儿子更新),Updata(叶节点直接更新)
儿子更新:
void Merge(int rt,int lch,int rch,int l,int r) {
int mid=(l+r)>>1;//Query为区间边权和
tree[rt].maxl=max(tree[lch].maxl,Query(l,mid+1)+tree[rch].maxl);
tree[rt].maxr=max(tree[rch].maxr,Query(mid,r)+tree[lch].maxr);
tree[rt].v=max(tree[lch].maxr+Query(mid,mid+1)+tree[rch].maxl,max(tree[lch].v,tree[rch].v));
}
叶节点更新:
利用叶节点只有一个点的特点直接用点的set更新
void Updata(int u,int id) {
int d1=-INF,d2=-INF;
if(ch[u].size()!=0)
d1=*(ch[u].rbegin());
if(ch[u].size()>1)
d2=*(++ch[u].rbegin());
if(col[u]==0) {
tree[id].maxl=tree[id].maxr=max(d1,0);
tree[id].v=max(0,max(d1,d1+d2));
}
else {
tree[id].maxl=tree[id].maxr=d1;
tree[id].v=max(-INF,d1+d2);
}
}
int Query(int l,int r) {//询问[l,r]区间内的边权和,利用性质:任何一个线段树内的dfn是连续的
if(l>r)
swap(l,r);
return presum[r]-presum[l];//dfn连续,可以用前缀和求得
}
动态更新(重链之间Dfs递归更新,重链之内线段更新):
先找到重链之间的道路:Find_path
path存路上的点,pathv存重链之间的距离,找到u到根节点的距离。在Modify之前使用。
void Find_path(int u) {
while(u!=0) {
path.push_back(u);
pathv.push_back(fav[top[u]]);
u=fa[top[u]];
}
}
用Modify更新,寻找的时候用i记录
更新时一定要将原答案删除再更新
void Modify(int l,int r,int id,int i,bool flag) {
if(l==r) {//叶节点
int u=rnk[l];
if(i!=0) {//路程没有走完
int nxt=top[path[i-1]];
ch[u].erase(ch[u].find(tree[root[nxt]].maxl+pathv[i-1]));//删除已经不存在的答案
Modify(tid[nxt],tid[nxt]+sz[nxt]-1,root[nxt],i-1,1);//更新
ch[u].insert(tree[root[nxt]].maxl+pathv[i-1]);//更新完后将答案存入
}
if(flag)
ansx.erase(ansx.find(tree[id].v));//删除旧答案
Updata(u,id);
if(flag)
ansx.insert(tree[id].v);//更新新答案
}
else {
int mid=(l+r)>>1;
if(tid[path[i]]<=mid)
Modify(l,mid,tree[id].pl,i,0);
else Modify(mid+1,r,tree[id].pr,i,0);
if(flag)
ansx.erase(ansx.find(tree[id].v));
Merge(id,tree[id].pl,tree[id].pr,l,r);
if(flag)
ansx.insert(tree[id].v);//原理类似于Build
}
}
#include
#include
#include
#include
#include
#include
#define MAXN 100010
#define INF (int)1e9+7
using namespace std;
int n,q,dcnt,cnt;
int tid[MAXN],top[MAXN],rnk[MAXN],sz[MAXN];
int fa[MAXN],son[MAXN],fav[MAXN],presum[MAXN*4];
int col[MAXN],root[MAXN];
multiset<int> ch[MAXN],ansx;
vector<int>G[MAXN],W[MAXN];
vector<int>path,pathv;
struct node{
int pl,pr,v,maxl,maxr;
}tree[MAXN*4];
int read(){
int f=1,x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
void Dfs1(int x,int fax) {
fa[x]=fax;
sz[x]=1;
son[x]=-1;
for(int i=0;iint v=G[x][i];
if(v!=fax) {
Dfs1(v,x);
sz[x]+=sz[v];
if(son[x]==-1||sz[son[x]]else fav[x]=W[x][i];
}
}
void Dfs2(int x,int tp) {
top[x]=tp;
tid[x]=++dcnt;
sz[tp]++;
if(x!=tp)
presum[tid[x]]=presum[tid[fa[x]]]+fav[x];//前缀和
rnk[dcnt]=x;
if(son[x]==-1) return ;
Dfs2(son[x],tp);
for(int i=0;iint v=G[x][i];
if(v!=son[x]&&v!=fa[x])
Dfs2(v,v);
}
}
int Query(int l,int r) {
if(l>r)
swap(l,r);
return presum[r]-presum[l];
}
void Merge(int rt,int lch,int rch,int l,int r) {
int mid=(l+r)>>1;
tree[rt].maxl=max(tree[lch].maxl,Query(l,mid+1)+tree[rch].maxl);
tree[rt].maxr=max(tree[rch].maxr,Query(mid,r)+tree[lch].maxr);
tree[rt].v=max(tree[lch].maxr+Query(mid,mid+1)+tree[rch].maxl,max(tree[lch].v,tree[rch].v));
}
void Updata(int u,int id) {
int d1=-INF,d2=-INF;
if(ch[u].size()!=0)
d1=*(ch[u].rbegin());
if(ch[u].size()>1)
d2=*(++ch[u].rbegin());
if(col[u]==0) {
tree[id].maxl=tree[id].maxr=max(d1,0);
tree[id].v=max(0,max(d1,d1+d2));
}
else {
tree[id].maxl=tree[id].maxr=d1;
tree[id].v=max(-INF,d1+d2);
}
}
void Build(int l,int r,int id,bool flag) {
if(l==r) {
int u=rnk[l];
for(int i=0;iint v=G[u][i];
if(v!=fa[u]&&top[u]!=top[v]) {
root[v]=++cnt;
Build(tid[v],tid[v]+sz[v]-1,root[v],1);
ch[u].insert(tree[root[v]].maxl+W[u][i]);
}
}
Updata(u,id);
if(flag==1)
ansx.insert(tree[id].v);
}
else {
int mid=(l+r)>>1;
tree[id].pl=++cnt;
tree[id].pr=++cnt;
Build(l,mid,tree[id].pl,0);
Build(mid+1,r,tree[id].pr,0);
Merge(id,tree[id].pl,tree[id].pr,l,r);
if(flag==1)
ansx.insert(tree[id].v);
}
}
void Find_path(int u) {
while(u!=0) {
path.push_back(u);
pathv.push_back(fav[top[u]]);
u=fa[top[u]];
}
}
void Modify(int l,int r,int id,int i,bool flag) {
if(l==r) {
int u=rnk[l];
if(i!=0) {//路程没有走完
int nxt=top[path[i-1]];
ch[u].erase(ch[u].find(tree[root[nxt]].maxl+pathv[i-1]));
Modify(tid[nxt],tid[nxt]+sz[nxt]-1,root[nxt],i-1,1);
ch[u].insert(tree[root[nxt]].maxl+pathv[i-1]);
}
if(flag)
ansx.erase(ansx.find(tree[id].v));
Updata(u,id);
if(flag)
ansx.insert(tree[id].v);
}
else {
int mid=(l+r)>>1;
if(tid[path[i]]<=mid)
Modify(l,mid,tree[id].pl,i,0);
else Modify(mid+1,r,tree[id].pr,i,0);
if(flag)
ansx.erase(ansx.find(tree[id].v));
Merge(id,tree[id].pl,tree[id].pr,l,r);
if(flag)
ansx.insert(tree[id].v);
}
}
int main() {
int u,v,w,sum=0;
char tag[5];
n=read();
for(int i=1;i//G:儿子节点
G[v].push_back(u),W[v].push_back(w);//W:(u,v)边权
}
Dfs1(1,0);
memset(sz,0,sizeof(sz));//将后来没有用的子树大小sz改为0,在DFS2中存top为i的重链长度sz[i]
Dfs2(1,1);
root[1]=cnt=1;//初始化起点及其编号
Build(tid[1],tid[1]+sz[1]-1,1,1);//对1号重链建树
q=read();
while(q--) {
scanf("%s",tag);
if(tag[0]=='C') {
u=read();
if(!col[u])
sum++;
else sum--;
col[u]^=1;
path.clear();
pathv.clear();
Find_path(u);//先找路径再修改
Modify(tid[1],tid[1]+sz[1]-1,root[1],path.size()-1,1);//在修改时使用dfn一般比较方便
}// dfn dfn id 路程 是否为top
else {
if(sum==n)
printf("They have disappeared.\n");
else if(sum==n-1)
printf("0\n");
else printf("%d\n",*ansx.rbegin());
}
}
return 0;
}