Luogu4115 Qtree4
https://www.luogu.com.cn/problem/P4115
这道题明显是动态点分治板子,但是用树剖写起来就显得十分困难了。
由于本题维护的是全局信息,我们不能仅从线段树上直接读取答案。
不过树链剖分维护的就是链信息,因此我们考虑如果是链应该怎么做。
求一段区间中的最远白点距离,这很容易维护,只需要求出这个区间离左端、右端的最近白点,同时记录整个区间的答案,就可以轻松合并。
我们把所有的重子树都用线段树维护,轻子树用\(multiset\)储存答案,那么我们修改时只需要沿着重链跳到根,每到轻重儿子交换的时候就修改\(multiset\)内的值,同时单点修改线段树中的值,这样的维护方式与动态dp有异曲同工之妙。
全局答案可以再开一个\(multiset\),同时修改。
这里线段树维护的值看起来十分奇怪,如果随便抽取一个区间求值,它可能是没有意义的,但是我们每次取一条重链的值,由于\(dfn\)序连续,读取的值必然是有意义的。
总时间复杂度:\(O(n \log^2 n)\)
\(n \log n\)的神仙做法暂时咕着吧。。。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#define N 100005
#define INF 1000000007
using namespace std;
int n,x,y,z,ct;
struct node
{
int nxt,v,cost;
node (int xx=0,int yy=0,int zz=0)
{
nxt=xx,v=yy,cost=zz;
}
}e[N << 1];
int tot,q,f[N],fr[N];
int sz[N],son[N],val[N],d[N];
int cnt,dfn[N],rdfn[N],t[N],dis[N];
int lst[N][2];
bool col[N];
char opt[5];
struct cmp
{
bool operator () (int x,int y)
{
return x>y;
}
};
multiset<int,cmp>s[N],ans;
void add(int x,int y,int z)
{
++tot;
e[tot]=node(fr[x],y,z),fr[x]=tot;
}
void dfs1(int u)
{
int mx=-1;
sz[u]=1;
for (int i=fr[u];i;i=e[i].nxt)
{
int v=e[i].v,cost=e[i].cost;
if (v==f[u])
continue;
dis[v]=dis[u]+cost;
val[v]=cost;
f[v]=u;
dfs1(v);
sz[u]+=sz[v];
if (sz[v]>mx)
mx=sz[v],son[u]=v;
}
}
void dfs2(int u,int tp)
{
dfn[u]=++cnt;
rdfn[cnt]=u;
t[u]=tp;
if (!son[u])
{
d[tp]=u;
return;
}
dfs2(son[u],tp);
for (int i=fr[u];i;i=e[i].nxt)
{
int v=e[i].v;
if (v==f[u] || v==son[u])
continue;
dfs2(v,v);
}
}
struct Mxdis
{
int l,r,v;
Mxdis (int L=0,int R=0,int V=0)
{
l=L,r=R,v=V;
}
}tr[N << 2];
Mxdis Merge(Mxdis a,Mxdis b,int L,int DL,int DR)
{
Mxdis c;
c.v=max(max(a.v,b.v),a.r+L+b.l);
c.l=max(a.l,DL+L+b.l);
c.r=max(b.r,DR+L+a.r);
return c;
}
int id(int x)
{
return rdfn[x];
}
void build(int p,int l,int r)
{
if (l==r)
return;
int mid=(l+r) >> 1;
build(p << 1,l,mid);
build(p << 1 | 1,mid+1,r);
tr[p]=Merge(tr[p << 1],tr[p << 1 | 1],val[id(mid+1)],dis[id(mid)]-dis[id(l)],dis[id(r)]-dis[id(mid+1)]);
}
void modify(int p,int l,int r,int x)
{
if (l==r)
{
int mx=-INF,se=-INF,tx=id(x);
if (!col[tx])
mx=se=0;
if (!s[tx].empty())
mx=max(mx,*s[tx].begin());
if (s[tx].size()>1)
se=max(se,*(++s[tx].begin()));
tr[p].l=tr[p].r=mx;
if (!col[tx])
tr[p].v=max(mx,mx+se); else
tr[p].v=max(-INF,mx+se);
return;
}
int mid=(l+r) >> 1;
if (x<=mid)
modify(p << 1,l,mid,x); else
modify(p << 1 | 1,mid+1,r,x);
tr[p]=Merge(tr[p << 1],tr[p << 1 | 1],val[id(mid+1)],dis[id(mid)]-dis[id(l)],dis[id(r)]-dis[id(mid+1)]);
}
Mxdis calc(int p,int l,int r,int x,int y)
{
if (l==x && r==y)
return tr[p];
int mid=(l+r) >> 1;
if (y<=mid)
return calc(p << 1,l,mid,x,y); else
if (x>mid)
return calc(p << 1 | 1,mid+1,r,x,y); else
return Merge(calc(p << 1,l,mid,x,mid),calc(p << 1 | 1,mid+1,r,mid+1,y),
val[id(mid+1)],dis[id(mid)]-dis[id(x)],dis[id(y)]-dis[id(mid+1)]);
}
int dfs3(int u)
{
for (int i=u;i;i=son[i])
{
for (int j=fr[i];j;j=e[j].nxt)
{
int v=e[j].v;
if (v==f[i] || v==son[i])
continue;
int k=dfs3(v);
s[i].insert(k+e[j].cost);
}
modify(1,1,n,dfn[i]);
}
Mxdis k=calc(1,1,n,dfn[u],dfn[d[u]]);
lst[u][0]=k.l,lst[u][1]=k.v;
ans.insert(k.v);
return k.l;
}
Mxdis calc2(int u)
{
return calc(1,1,n,dfn[u],dfn[d[u]]);
}
void change(int x)
{
Mxdis z;
for (;;)
{
modify(1,1,n,dfn[x]);
z=calc2(t[x]);
ans.erase(ans.find(lst[t[x]][1]));
ans.insert(z.v);
lst[t[x]][1]=z.v;
if (!f[t[x]])
{
lst[t[x]][0]=z.l;
return;
}
int tt=f[t[x]];
s[tt].erase(s[tt].find(lst[t[x]][0]+val[t[x]]));
s[tt].insert(z.l+val[t[x]]);
lst[t[x]][0]=z.l;
x=f[t[x]];
}
}
int main()
{
scanf("%d",&n),ct=n;
for (int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
dfs1(1);
dfs2(1,1);
build(1,1,n);
dfs3(1);
scanf("%d",&q);
while (q--)
{
scanf("%s",opt);
if (opt[0]=='A')
{
if (!ct)
puts("They have disappeared."); else
if (ct==1)
puts("0"); else
printf("%d\n",*ans.begin());
} else
{
scanf("%d",&x);
if (col[x])
++ct; else
--ct;
col[x]=!col[x];
change(x);
}
}
return 0;
}