【清华集训2016】数据交互
比较神的\(DDP\)。
首先对于给出的一条链我们分两部分统计:\(lca\)以及其他部分。
我们设两个变量\(w_i,g_i\)。一条路径的权值就是路径上所有点的\(w\)之和\(+g_{lca}\)。所以我们将修改\(w_{lca}\)的权值,修改其他点的\(g\)值。删除的时候就加一条相同的,权值相反的路径。
具体细节就有多了。
首先我们给每个点\(v\)开一个可删除堆表示\(v\)的虚子树中到\(v\)的最长链。再开一个全局的可删除堆记录每条链贡献的答案。我们发现,一条路径可以看成是重链上的\(u,v\)两点(其中\(u\)是\(lca\)),以及这两点分别向虚子树伸出去的一条最长链(可以为空)。它的权值为这条链的\(g\)之和再加上\(w_u\)。当\(u\)等于\(v\)的时候,我们需要找最长链和次长链。
我们发现这就是一个最大连续子段和。
对于线段树上的一个节点,我们记录:
\(mx\)表示答案。
\(lmx\)表示从最左边代表的节点(深度最小)开始的最长链。
\(rmx\)表示从最右边代表的节点(深度最大)开始的最长链。
还要记录\(sum\)表示区间内所有节点的\(w\)只和。
一条重链的链顶的最长链就是这条重链对应的\(lmx\)。
修改的时候暴力将\(lca\)以上\(log\)条重链原来对其父亲贡献的最长链删除掉。更新过后将新的最长链贡献上去。
更多细节就看代码吧:
#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
int n,m;
struct road {int to,next;}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}
int fa[N],dep[N];
int size[N],son[N];
void dfs(int v) {
size[v]=1;
for(int i=h[v];i;i=s[i].next) {
int to=s[i].to;
if(to==fa[v]) continue ;
dep[to]=dep[v]+1;
fa[to]=v;
dfs(to);
if(size[son[v]]<size[to]) son[v]=to;
size[v]+=size[to];
}
}
int dfn[N],bot[N],top[N],id;
int lst[N];
void dfs2(int v,int tp) {
dfn[v]=++id;
lst[id]=v;
top[v]=tp;
bot[v]=v;
if(son[v]) {
dfs2(son[v],tp);
bot[v]=bot[son[v]];
}
for(int i=h[v];i;i=s[i].next) {
int to=s[i].to;
if(to==fa[v]||to==son[v]) continue ;
dfs2(to,to);
}
}
struct tree {
int l,r;
ll lmx,rmx;
ll mx,tag;
ll sum;
tree() {l=r=lmx=rmx=mx=tag=sum=0;}
}tr[N<<2];
struct Heap {
priority_queue<ll>add,del;
void Pop() {
while(del.size()&&add.top()==del.top()) {
add.pop();
del.pop();
}
}
void Push(ll x) {
Pop();
add.push(x);
}
void Del(ll x) {
del.push(x);
}
ll Top() {
Pop();
return add.top();
}
ll Sec() {
ll x=Top();
Del(x);
ll y=Top();
Push(x);
return y;
}
}Ans,F[N];
tree operator +(const tree &a,const tree &b) {
tree tem;
tem.l=a.l,tem.r=b.r;
tem.mx=max(a.rmx+b.lmx,max(a.mx,b.mx));
tem.lmx=max(a.lmx,a.sum+b.lmx);
tem.rmx=max(b.rmx,a.rmx+b.sum);
tem.sum=a.sum+b.sum;
return tem;
}
// sum 单点
//tag 链
void Update(int v,int p) {
int u=lst[p];
ll fir=F[u].Top(),sec=F[u].Sec();
tr[v].lmx=tr[v].sum+fir;
tr[v].rmx=tr[v].tag+tr[v].sum+fir;
tr[v].mx=tr[v].tag+tr[v].sum+fir+sec;
}
void build(int v,int l,int r) {
tr[v].l=l,tr[v].r=r;
if(l==r) {
Update(v,l);
return ;
}
int mid=l+r>>1;
build(v<<1,l,mid),build(v<<1|1,mid+1,r);
tr[v]=tr[v<<1]+tr[v<<1|1];
}
void Set(int v,ll f) {
tr[v].mx+=f;
tr[v].tag+=f;
// tr[v].lmx+=f;
tr[v].rmx+=f;
}
void down(int v) {
if(tr[v].tag) {
Set(v<<1,tr[v].tag),Set(v<<1|1,tr[v].tag);
tr[v].tag=0;
}
}
void add_mx(int v,int l,int r,ll f) {//修改给出的整条链
if(tr[v].l>r||tr[v].r<l) return ;
if(l<=tr[v].l&&tr[v].r<=r) {
Set(v,f);
return ;
}
down(v);
add_mx(v<<1,l,r,f),add_mx(v<<1|1,l,r,f);
tr[v]=tr[v<<1]+tr[v<<1|1];
}
void add_sum(int v,int p,int f) {//修改lca
if(tr[v].l>p||tr[v].r<p) return ;
tr[v].sum+=f;
if(tr[v].l==tr[v].r) {
tr[v].lmx+=f;
tr[v].rmx+=f;
tr[v].mx+=f;
return ;
}
down(v);
add_sum(v<<1,p,f),add_sum(v<<1|1,p,f);
tr[v]=tr[v<<1]+tr[v<<1|1];
}
void update(int v,int p) {
if(tr[v].l>p||tr[v].r<p) return ;
if(tr[v].l==tr[v].r) {
Update(v,p);
return ;
}
down(v);
update(v<<1,p),update(v<<1|1,p);
tr[v]=tr[v<<1]+tr[v<<1|1];
}
tree query(int v,int l,int r) {
if(l<=tr[v].l&&tr[v].r<=r) return tr[v];
down(v);
int mid=tr[v].l+tr[v].r>>1;
if(r<=mid) return query(v<<1,l,r);
else if(l>mid) return query(v<<1|1,l,r);
else return query(v<<1,l,r)+query(v<<1|1,l,r);
}
int lca(int a,int b) {
while(top[a]!=top[b]) {
if(dep[top[a]]<dep[top[b]]) swap(a,b);
a=fa[top[a]];
}
return dep[a]<dep[b]?a:b;
}
void Modify_chain(int a,int b,int f) {
while(top[a]!=top[b]) {
if(dep[top[a]]<dep[top[b]]) swap(a,b);
add_mx(1,dfn[top[a]],dfn[a],f);
a=fa[top[a]];
}
if(dep[a]>dep[b]) swap(a,b);
add_mx(1,dfn[a],dfn[b],f);
}
void Del(int a,int b) {
static tree tem;
while(top[a]!=top[b]) {
if(dep[top[a]]<dep[top[b]]) swap(a,b);
tem=query(1,dfn[top[a]],dfn[bot[a]]);
Ans.Del(tem.mx);
a=fa[top[a]];
}
tem=query(1,dfn[top[a]],dfn[bot[a]]);
Ans.Del(tem.mx);
}
void ADD(int a,int b) {
static tree tem;
while(top[a]!=top[b]) {
if(dep[top[a]]<dep[top[b]]) swap(a,b);
tem=query(1,dfn[top[a]],dfn[bot[a]]);
Ans.Push(tem.mx);
a=fa[top[a]];
}
tem=query(1,dfn[top[a]],dfn[bot[a]]);
Ans.Push(tem.mx);
}
void Modify(int a,int b,int w) {
int f=lca(a,b);
tree tem;
for(int i=top[f];i;i=top[fa[i]]) {
tem=query(1,dfn[i],dfn[bot[i]]);
if(fa[i]) F[fa[i]].Del(tem.lmx);
if(i!=top[f]) {
Ans.Del(tem.mx);
}
}
Del(a,b);
add_sum(1,dfn[f],w);
Modify_chain(a,b,w);
Modify_chain(f,f,-w);
ADD(a,b);
for(int i=top[f];i;i=top[fa[i]]) {
tem=query(1,dfn[i],dfn[bot[i]]);
if(fa[i]) {
F[fa[i]].Push(tem.lmx);
update(1,dfn[fa[i]]);
}
if(i!=top[f]) Ans.Push(tem.mx);
}
}
struct edge {int x,y,w;}e[N];
void out(int v) {
int x=top[v];
for(int i=dfn[x];;i++) {
cout<<lst[i]<<" ";
if(i==dfn[v]) return ;
}
}
int main() {
n=Get(),m=Get();
int a,b;
for(int i=1;i<n;i++) {
a=Get(),b=Get();
add(a,b),add(b,a);
}
dfs(1);
dfs2(1,1);
for(int i=1;i<=n;i++) F[i].Push(0),F[i].Push(0);
build(1,1,n);
for(int i=1;i<=n;i++) if(i==top[i]) Ans.Push(0);
char op;
for(int i=1;i<=m;i++) {
while(op=getchar(),op!='+'&&op!='-');
if(op=='+') {
e[i].x=Get(),e[i].y=Get(),e[i].w=Get();
Modify(e[i].x,e[i].y,e[i].w);
} else {
int id=Get();
Modify(e[id].x,e[id].y,-e[id].w);
}
cout<<Ans.Top()<<"\n";
}
return 0;
}