• UOJ #268 BZOJ 4732 [清华集训2016]数据交互 (树链剖分、线段树)


    题目链接

    (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4732
    (UOJ) http://uoj.ac/problem/268

    题解

    首先考虑,给定一条路径,如何计算与其相交的所有路径的权值和?显然一条路径和另一条路径相交,当且仅当这条路径的LCA在另一条路径上,或者另一条路径的LCA在这条路径上。那么我们考虑维护两个数组(a)(b), 分别表示以某点为LCA的路径权值和以及覆盖这个点但不以该点为LCA的路径权值和,则路径((u,v))的价值为(sum_{p in (u,v)}b_p+a_{LCA(u,v)}).
    下面我们要求路径的最大权值,考虑链分治。在一条重链上统计LCA在这条重链上的链的答案。假设一条链和该重链相交的部分为((u,v))其中(u)(v)的祖先也是链的LCA。若(u e v), 该链的权值为(h_u+h_v+sum^v_{i=u}a_i+b_u),否则为(h_u+h'_u+a_u+b_u), 其中(h_u)(h'_u)分别为(u)点的轻儿子引出的链的(sum a_i)的最大值和次大值。一条重链的权值等于上面的式子在(u)(v)变化时的最大值,考虑维护这个最大值,对于(u e v)的情况,需要采取最大子段和的维护方式;对于(u=v)的情况,就是简单的区间修改区间最大值,都可以用线段树维护。总答案等于所有重链的权值最大值,因此我们开一个multiset维护所有重链的权值。
    考虑修改。修改时我们需要把一条链上的(b)增加一个权值(w)(可能是负数),再把LCA处的(a)增加一个权值(w). 修改(b)在重链上是区间修改,这个可以简单地进行打标记,把后缀和和最大子段和加上某一个值。修改(a)在重链上是单点修改。但是一个(a)的修改还会影响到它所在重链顶端的父亲的(h), 那个点的(h)改变又会影响它所在重链顶端的父亲的(h)……直到根,因此需要不断跳重链修改。这条链的(sum a_i)最大值就相当于我们在线段树中维护的“最大前缀和”那个量((sum^v_{i=u}a_i+h_v)), 因此可以直接重新查出,设为(sa_i). 为了快速维护(sa)的最大次大,我们只需对每个点再开一个multiset记录一下即可。每次对一条链进行修改后,需要重新计算这条链的权值,更新总答案multiset.
    时间复杂度(O(nlog^2n)).
    注意multiset用两个堆实现会快,线段树给每条链开一棵比([1,n])开一棵要快。

    代码

    人丑常数大……

    #include<bits/stdc++.h>
    #define llong long long
    using namespace std;
    
    inline int read()
    {
    	int w=1,s=0;char ch=getchar();
    	while(!isdigit(ch)) {if(ch=='-')w=-1;ch=getchar();}
    	while(isdigit(ch)) {s=s*10+ch-'0';ch=getchar();}
    	return w*s;
    }
    
    const int N = 1e5;
    struct Edge
    {
    	int nxt,v;
    } e[(N<<1)+3];
    struct Query
    {
    	int u,v,w;
    } qr[N+3];
    int fe[N+3];
    int fa[N+3];
    int dfn[N+3],idfn[N+3];
    int dep[N+3];
    int sz[N+3];
    int hvs[N+3];
    int tpn[N+3],btn[N+3];
    llong a[N+3],h[N+3],h2[N+3],hv[N+3],sa[N+3];
    int n,q,en,dfnn;
    
    struct Multiset
    {
    	priority_queue<llong> q1,q2;
    	void insert(llong x) {q1.push(x);}
    	void erase(llong x) {q2.push(x);}
    	llong getmx()
    	{
    		while(!q2.empty()&&q1.top()==q2.top()) {q1.pop(),q2.pop();}
    		return q1.top();
    	}
    	llong getmx2()
    	{
    		llong mx = getmx(); erase(mx); llong ret = getmx(); insert(mx); return ret;
    	}
    };
    Multiset lt[N+3]; int ltn[N+3];
    Multiset s;
    
    void addedge(int u,int v)
    {
    	en++; e[en].v = v;
    	e[en].nxt = fe[u]; fe[u] = en;
    }
    
    void dfs1(int u)
    {
    	sz[u] = 1; hvs[u] = 0;
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v==fa[u]) continue;
    		fa[v] = u;
    		dep[v] = dep[u]+1;
    		dfs1(v);
    		sz[u] += sz[v];
    		if(hvs[u]==0||sz[v]>sz[hvs[u]]) {hvs[u] = v;}
    	}
    }
    void dfs2(int u)
    {
    	dfn[u] = ++dfnn; idfn[dfnn] = u;
    	if(!hvs[u]) {btn[u] = u; s.insert(0ll); return;}
    	tpn[hvs[u]] = tpn[u]; dfs2(hvs[u]); btn[u] = btn[hvs[u]];
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v==fa[u]||v==hvs[u]) continue;
    		lt[u].insert(0); ltn[u]++;
    		tpn[v] = v;
    		dfs2(v);
    	}
    }
    
    struct Data
    {
    	llong v,lv,rv,lrv;
    	Data() {v = lv = rv = lrv = 0ll;}
    	Data(llong _v,llong _lv,llong _rv,llong _lrv):v(_v),lv(_lv),rv(_rv),lrv(_lrv) {}
    };
    Data operator +(const Data &x,const Data &y)
    {
    	Data ret;
    	ret.lrv = x.lrv+y.lrv;
    	ret.lv = max(x.lv,x.lrv+y.lv);
    	ret.rv = max(y.rv,x.rv+y.lrv);
    	ret.v = max(max(x.v,y.v),x.rv+y.lv);
    	return ret;
    }
    struct SegmentTree
    {
    	struct SgTNode
    	{
    		Data x; llong tag;
    	} sgt[(N<<2)+3];
    	void maketag(int u,llong tag)
    	{
    		sgt[u].x.rv += tag; sgt[u].x.v += tag;
    		sgt[u].tag += tag;
    	}
    	void pushdown(int u)
    	{
    		llong tag = sgt[u].tag;
    		if(tag)
    		{
    			maketag(u<<1,tag); maketag(u<<1|1,tag);
    			sgt[u].tag = 0ll;
    		}
    	}
    	void upd(int u,int le,int ri,int pos)
    	{
    		if(le==ri) {int pos0 = idfn[pos]; sgt[u].x = Data(a[pos0]+sgt[u].tag+h[pos0],a[pos0]+h[pos0],a[pos0]+sgt[u].tag+h[pos0],a[pos0]); return;}
    		pushdown(u);
    		int mid = (le+ri)>>1;
    		if(pos<=mid) upd(u<<1,le,mid,pos);
    		else upd(u<<1|1,mid+1,ri,pos);
    		sgt[u].x = sgt[u<<1].x+sgt[u<<1|1].x;
    	}
    	void addb(int u,int le,int ri,int lb,int rb,llong x)
    	{
    		if(le>=lb && ri<=rb) {maketag(u,x); return;}
    		pushdown(u);
    		int mid = (le+ri)>>1;
    		if(lb<=mid) addb(u<<1,le,mid,lb,rb,x);
    		if(rb>mid) addb(u<<1|1,mid+1,ri,lb,rb,x);
    		sgt[u].x = sgt[u<<1].x+sgt[u<<1|1].x;
    	}
    	Data query(int u,int le,int ri,int lb,int rb)
    	{
    		if(le>=lb && ri<=rb) {return sgt[u].x;}
    		pushdown(u);
    		int mid = (le+ri)>>1; Data ret;
    		if(rb<=mid) ret = query(u<<1,le,mid,lb,rb);
    		else if(lb>mid) ret = query(u<<1|1,mid+1,ri,lb,rb);
    		else ret = query(u<<1,le,mid,lb,rb)+query(u<<1|1,mid+1,ri,lb,rb);
    		sgt[u].x = sgt[u<<1].x+sgt[u<<1|1].x;
    		return ret;
    	}
    } sgt;
    struct SegmentTree2
    {
    	struct SgTNode
    	{
    		llong tag,mx;
    	} sgt[(N<<2)+3];
    	void maketag(int u,llong tag)
    	{
    		sgt[u].mx += tag; sgt[u].tag += tag;
    	}
    	void pushdown(int u)
    	{
    		llong tag = sgt[u].tag;
    		if(tag)
    		{
    			maketag(u<<1,tag); maketag(u<<1|1,tag);
    			sgt[u].tag = 0;
    		}
    	}
    	void add(int u,int le,int ri,int lb,int rb,llong x)
    	{
    		if(le>=lb && ri<=rb) {maketag(u,x); return;}
    		pushdown(u);
    		int mid = (le+ri)>>1;
    		if(lb<=mid) add(u<<1,le,mid,lb,rb,x);
    		if(rb>mid) add(u<<1|1,mid+1,ri,lb,rb,x);
    		sgt[u].mx = max(sgt[u<<1].mx,sgt[u<<1|1].mx);
    	}
    	llong query(int u,int le,int ri,int lb,int rb)
    	{
    		if(le>=lb && ri<=rb) {return sgt[u].mx;}
    		pushdown(u);
    		int mid = (le+ri)>>1; llong ret = 0ll;
    		if(lb<=mid) ret = max(ret,query(u<<1,le,mid,lb,rb));
    		if(rb>mid) ret = max(ret,query(u<<1|1,mid+1,ri,lb,rb));
    		sgt[u].mx = max(sgt[u<<1].mx,sgt[u<<1|1].mx);
    		return ret;
    	}
    } sgt2;
    
    void calcpath(int u)
    {
    	int x = tpn[u],y = btn[u];
    	s.erase(hv[x]);
    	Data tmp = sgt.query(1,1,n,dfn[x],dfn[y]); hv[x] = tmp.v;
    	llong tmp2 = sgt2.query(1,1,n,dfn[x],dfn[y]); hv[x] = max(hv[x],tmp2);
    	s.insert(hv[x]);
    }
    void addvpath(int u,int v,llong w)
    {
    	sgt.addb(1,1,n,dfn[u],dfn[v],w);
    	sgt2.add(1,1,n,dfn[u],dfn[v],w);
    	calcpath(u);
    }
    void addpath(int u0,int v0,llong w)
    {
    	int u = u0,v = v0;
    	while(tpn[u]!=tpn[v])
    	{
    		if(dep[tpn[u]]>dep[tpn[v]]) {addvpath(tpn[u],u,w); u = fa[tpn[u]];}
    		else {addvpath(tpn[v],v,w); v = fa[tpn[v]];}
    	}
    	if(dep[u]>dep[v]) swap(u,v);
    	if(u!=v) {addvpath(idfn[dfn[u]+1],v,w);}
    	a[u] += w; sgt.upd(1,1,n,dfn[u]); sgt2.add(1,1,n,dfn[u],dfn[u],w); calcpath(u);
    	int x = fa[tpn[u]];
    	while(x)
    	{
    		lt[x].erase(sa[tpn[u]]);
    		Data tmp = sgt.query(1,1,n,dfn[tpn[u]],dfn[btn[u]]); sa[tpn[u]] = tmp.lv;
    		lt[x].insert(sa[tpn[u]]);
    		llong tmp1 = h[x]; h[x] = lt[x].getmx(); sgt.upd(1,1,n,dfn[x]); sgt2.add(1,1,n,dfn[x],dfn[x],h[x]-tmp1);
    		if(ltn[x]>=2)
    		{
    			llong tmp2 = h2[x]; h2[x] = lt[x].getmx2();
    			sgt2.add(1,1,n,dfn[x],dfn[x],h2[x]-tmp2);
    		}
    		calcpath(x);
    		u = x; x = fa[tpn[x]];
    	}
    }
    
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for(int i=1; i<n; i++)
    	{
    		int u,v; scanf("%d%d",&u,&v);
    		addedge(u,v); addedge(v,u);
    	}
    	dep[1] = 1; dfs1(1);
    	tpn[1] = 1; dfs2(1);
    	for(int i=1; i<=q; i++)
    	{
    		char opt[5]; scanf("%s",opt); int u,v,w;
    		if(opt[0]=='+') {scanf("%d%d%d",&qr[i].u,&qr[i].v,&qr[i].w); u = qr[i].u,v = qr[i].v,w = qr[i].w;}
    		else {int id; scanf("%d",&id); u = qr[id].u,v = qr[id].v,w = -qr[id].w;}
    		addpath(u,v,w);
    		printf("%lld
    ",s.getmx());
    	}
    	return 0;
    }
    
  • 相关阅读:
    仪仗队
    疫情控制
    Code
    距离咨询
    舒适的路线
    桐桐的糖果计划
    跑路
    最短路计数
    骑马修栅栏
    搭桥
  • 原文地址:https://www.cnblogs.com/suncongbo/p/12058268.html
Copyright © 2020-2023  润新知