1 条题解

  • 0
    @ 2025-8-24 22:22:55

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar jun头吉吉
    alive

    搬运于2025-08-24 22:22:55,当前版本为作者最后更新于2022-06-12 21:45:52,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题意

    给定一棵 nn 个点的树,mm 次操作:

    • 添加一条路径 (ui,vi)(u_i,v_i),权值为 wiw_i
    • 撤回一次操作

    每一次操作后求出一条路径使得与这条路径有交的路径的权值和最大。

    题解

    100多通过没有题解非常恐怖。来献个丑。

    下面只考虑加路径操作,删路径增加一条相反数即可。

    下面记 x(u,v)x\in (u,v) 表示 xx 在这条路径上。

    首先对于一条路径 (u,v)(u,v),我们如果简洁地表示出所有与它有交的路径捏?发现有两种 (a,b)(a,b) 满足条件:

    • LCA(a,b)(u,v)\mathrm{LCA}(a,b)\in (u,v)LCA(a,b)LCA(u,v)\mathrm{LCA}(a,b)\ne\mathrm{LCA}(u,v)
    • LCA(u,v)(a,b)\mathrm{LCA}(u,v)\in (a,b)

    发现所有有交的 (a,b)(a,b) 在上面这个东西中不重不漏得被数到了一次。换成权值这个显然也是不重不漏的。于是我们可以记 val2ival2_i 表示所有 LCA(u,v)=i\mathrm{LCA}(u,v)=i 的权值和,val1ival1_i 表示所有 i(u,v)i\in(u,v)ww 的权值和。然后我们可以把一条路径有交的权值和写成非常漂亮的形式:路径上除了 LCA\rm LCAval2val_2 的和加上 val1LCAval1_{\mathrm{LCA}}

    然后这个就可以直接做了吧。设 fuf_u 表示从 uu 开始往下走最大的 val2val_2 和,然后转移 uu 的时候就算一个儿子的 ff 的最大值 mxmx 和次大值 cmxcmx,转移就是:

    $$\begin{aligned} &f_u\leftarrow val2_u+mx\\ &ans\leftarrow val1_u+mx+cmx \end{aligned} $$

    这个直接做就是 O(nm)\mathcal O(nm) 了,期望得分 30pts

    然后这个东西可以 DDP\rm DDP 吧。我们把重链上的转移写成矩阵,每一个节点开一个 multiset 记录不是重儿子的 ff 值,记 mxmx 为不是重儿子上的最大的 ffcmxcmx 为不是重儿子的次大值,那么:

    $$\begin{pmatrix} f_u\\ g_u\\ 0\end{pmatrix} =\begin{pmatrix} val2_u&-\infty& mx+val2_u\\ mx+val1_i&0&mx+cmx+val1_u\\ -\infty&\infty&0 \end{pmatrix} \times \begin{pmatrix} f_{heavy}\\ g_{heavy}\\ 0 \end{pmatrix} $$

    这里的矩阵乘法是对应加起来然后取 max\max

    其中 ff 的意思和上面相同,gg 的意思是这条重链到当前为止的最大权值和。全局开一个 multiset 记录所有重链的最大值。

    然后对这个矩阵我们考虑 val1val1val2val2 的修改。

    val2val2 的修改其实是简单的因为只会更改一个值,修改这个值暴力更改上去即可,复杂度 O(log2n)\mathcal O(\log^2n)

    然后 val1val1 的修改需要思考一下,矩阵会区间加一个值就会比较麻烦。但是思考一下也不是很麻烦,因为:

    $$\begin{pmatrix} a&-\infty&b\\ c&0&d\\ -\infty&\infty&0 \end{pmatrix}\times \begin{pmatrix} e&-\infty&f\\ g&0&h\\ -\infty&-\infty&0 \end{pmatrix} =\begin{pmatrix} a+e&-\infty&\max(a+f,b)\\ \max(e+c,g)&0&\max(c+f,h,d)\\ -\infty&-\infty&0 \end{pmatrix} $$

    发现区间加就是让 c,d,g,hc,d,g,h 全部加一个值,发现乘法之后这两个位置仍然是加这个值。然后就可以打懒标记做了。复杂度 O(log2n)\mathcal O(\log^2n)

    实现的时候为了方便可以差分成 44 次做,具体实现可以看代码。

    代码

    #include<bits/stdc++.h>
    #define mp make_pair
    #define mt make_tuple
    #define eb emplace_back
    #define pb push_back
    #define pc putchar
    #define chkmx(a,b) (a)=max((a),(b))
    #define chkmn(a,b) (a)=min((a),(b))
    #define fi first
    #define se second
    using namespace std;
    template<class T>
    void read(T&x){x=0;char c=getchar();bool f=0;for(;!isdigit(c);c=getchar())f^=c=='-';for(;isdigit(c);c=getchar())x=x*10+(c-'0');if(f)x=-x;}
    template<class T,class ...ARK>void read(T&x,ARK&...ark){read(x);read(ark...);}
    template<class T>void write(T x){if(x<0)pc('-'),x=-x;if(x>=10)write(x/10);pc(x%10+'0');}
    template<class T,class ...ARK>void write(T x,ARK...ark){write(x);pc(' ');write(ark...);}
    template<class ...ARK>void writeln(ARK...ark){write(ark...);pc('\n');}
    typedef long long ll;
    #define lowbit(x) ((x)&-(x))
    #define mid ((l+r)>>1)
    #define lc (x<<1)
    #define rc (x<<1|1)
    const int N=1e5+10;
    int n,m;
    vector<int>e[N];
    int fa[N],dep[N],dfn[N],sz[N],top[N],son[N],cnt,down[N];
    void dfs1(int u){
    	dep[u]=dep[fa[u]]+1;
    	sz[u]=1;
    	for(auto v:e[u])if(v!=fa[u]){
    		fa[v]=u;dfs1(v);sz[u]+=sz[v];
    		if(sz[v]>sz[son[u]])son[u]=v;
    	}
    }
    void dfs2(int u){
    	dfn[u]=++cnt;
    	if(!top[u]){top[u]=down[u]=u;while(son[down[u]])down[u]=son[down[u]];}
    	if(son[u])top[son[u]]=top[u],dfs2(son[u]);
    	for(auto v:e[u])if(v!=fa[u]&&v!=son[u])dfs2(v);
    }
    struct mat{
    	//  a -oo  b
    	//  c   0  d
    	//-oo -oo  0
    	ll a,b,c,d;
    	mat(ll a=0,ll b=0,ll c=0,ll d=0):a(a),b(b),c(c),d(d){}
    	friend mat operator*(mat A,mat B){
    		return mat(
    			A.a+B.a,
    			max(A.a+B.b,A.b),
    			max(A.c+B.a,B.c),
    			max({A.c+B.b,B.d,A.d})
    		);
    	}
    };
    struct node{
    	mat mt;ll tag;
    }t[N<<2];
    void pushtag(int x,ll v){
    	t[x].tag+=v;
    	t[x].mt.c+=v;
    	t[x].mt.d+=v;
    }
    void pushdown(int x){
    	if(t[x].tag)
    		pushtag(lc,t[x].tag),
    		pushtag(rc,t[x].tag),
    		t[x].tag=0;
    }
    void pushup(int x){t[x].mt=t[lc].mt*t[rc].mt;}
    void add(int x,int l,int r,int ql,int qr,int v){
    	if(ql<=l&&r<=qr)return pushtag(x,v);
    	if(r<ql||qr<l)return;
    	pushdown(x);
    	add(lc,l,mid,ql,qr,v);
    	add(rc,mid+1,r,ql,qr,v);
    	pushup(x);
    }
    void mdf(int x,int l,int r,int p,mat v){
    	if(l==r)return t[x].mt=v,void();
    	pushdown(x);
    	if(p<=mid)mdf(lc,l,mid,p,v);
    	else mdf(rc,mid+1,r,p,v);
    	pushup(x);
    }
    mat qry(int x,int l,int r,int ql,int qr){
    	if(ql<=l&&r<=qr)return t[x].mt;
    	pushdown(x);
    	if(qr<=mid)return qry(lc,l,mid,ql,qr);
    	if(mid<ql)return qry(rc,mid+1,r,ql,qr);
    	return qry(lc,l,mid,ql,qr)*qry(rc,mid+1,r,ql,qr);
    }
    ll get(int x,int l,int r,int p){
    	if(l==r)return t[x].tag;
    	pushdown(x);
    	if(p<=mid)return get(lc,l,mid,p);
    	else return get(rc,mid+1,r,p);
    }
    ll val2[N];
    multiset<ll>fl[N],res;
    mat getmat(int u){
    	ll v1=get(1,1,n,dfn[u]);
    	ll v2=val2[u];
    	ll mx=*fl[u].rbegin(),cmx=*++fl[u].rbegin();
    	return mat(v2,mx+v2,mx+v1,mx+cmx+v1);
    }
    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]];
    	}
    	if(dep[u]>dep[v])return v;
    	return u;
    }
    void addv1(int u,int w){
    	//从 u 到 根的路径 加 w
    	int x=u;
    	while(x){
    		mat ori=qry(1,1,n,dfn[top[x]],dfn[down[top[x]]]);
    		ll f=max(ori.a,ori.b),g=max(ori.c,ori.d);
    		res.erase(res.find(g));
    		if(fa[top[x]])fl[fa[top[x]]].erase(fl[fa[top[x]]].find(f));
    		x=fa[top[x]];
    	}
    	x=u;
    	while(x){
    		add(1,1,n,dfn[top[x]],dfn[x],w);
    		mat now=qry(1,1,n,dfn[top[x]],dfn[down[top[x]]]);
    		ll f=max(now.a,now.b),g=max(now.c,now.d);
    		res.insert(g);
    		if(fa[top[x]]){
    			fl[fa[top[x]]].insert(f);
    			mdf(1,1,n,dfn[fa[top[x]]],getmat(fa[top[x]]));
    		}
    		x=fa[top[x]];
    	}
    }
    void addv2(int u,int w){
    	//给 u 的 v2 加上 w
    	int x=u;
    	while(x){
    		mat ori=qry(1,1,n,dfn[top[x]],dfn[down[top[x]]]);
    		ll f=max(ori.a,ori.b),g=max(ori.c,ori.d);
    		res.erase(res.find(g));
    		if(fa[top[x]])
    			fl[fa[top[x]]].erase(fl[fa[top[x]]].find(f));
    		x=fa[top[x]];
    	}
    	val2[u]+=w;
    	mdf(1,1,n,dfn[u],getmat(u));
    	x=u;
    	while(x){
    		mat now=qry(1,1,n,dfn[top[x]],dfn[down[top[x]]]);
    		ll f=max(now.a,now.b),g=max(now.c,now.d);
    		res.insert(g);
    		if(fa[top[x]]){
    			fl[fa[top[x]]].insert(f);
    			mdf(1,1,n,dfn[fa[top[x]]],getmat(fa[top[x]]));
    		}
    		x=fa[top[x]];
    	}
    }
    void add(int u,int v,int w){
    	int l=lca(u,v);
    	addv1(u,w);addv1(v,w);addv1(l,-w);addv1(fa[l],-w);
    	addv2(l,w);
    }
    int x[N],y[N],w[N];
    signed main(){
    	//freopen("1.in","r",stdin);
    	//freopen("1.out","w",stdout);
    	read(n,m);
    	for(int i=1,u,v;i<n;i++)read(u,v),e[u].pb(v),e[v].pb(u);
    	dfs1(1);dfs2(1);
    	for(int i=1;i<=n;i++)fl[i].insert(0),fl[i].insert(0);
    	for(int i=1;i<=n;i++)for(auto j:e[i])if(j!=son[i]&&j!=fa[i])fl[i].insert(0);
    	for(int i=1;i<=n;i++)if(i==top[i])res.insert(0);
    	for(int i=1;i<=m;i++){
    		char op=getchar();while(op!='+'&&op!='-')op=getchar();
    		if(op=='+'){
    			read(x[i],y[i],w[i]);
    			add(x[i],y[i],w[i]);
    		}else{
    			int t;read(t);
    			add(x[t],y[t],-w[t]);
    		}
    		//for(int i=1;i<=n;i++)write(get(1,1,n,dfn[i])),pc(' ');pc('\n');
    		writeln(*res.rbegin());
    	}
    }
    
    • 1

    信息

    ID
    5649
    时间
    3000ms
    内存
    500MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者