【Comet OJ - Contest #8 F 黃金體驗】【lct】

題意

有一棵nn個節點的樹,每個點有一個初始權值wiw_i,要求支持兩種操作:
1、使xx的點權增加yy
2、給出kk,選定kk個點使得包含這kk個點的最小聯通子圖點權和最大,你只需要輸出這個最大值。
n,q105n,q\le10^5

分析

k=2k=2,顯然選的是帶權最長鏈。
可以發現kk每增加11,新的答案顯然是在原來最優方案的基礎上,新加入一個葉節點。
暴力的做法是先找到帶權最長鏈,使鏈上點的點權變爲00,然後每次找從直徑端點開始的最長鏈,再把最長鏈上點權變爲00.
可以發現這個過程實際上就是從帶權直徑的一個端點爲根作長鏈剖分,然後選出前kk大的鏈作爲答案。
可以用lct來維護長鏈剖分,每次更新點權後,不斷往上更新長鏈,相當於lct的access操作。
由於必須要以帶權直徑的端點作爲根,所以修改點權後可能要進行換根操作。具體實現的時候可以在access到直徑的時候,選出對應點較大的一端包含的端點換爲根,然後正常維護即可。
還要用數據結構來記錄每一條鏈的權值。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>

typedef long long LL;

const int N=100005;
const LL inf=(LL)1e14;

int n,q,rt,cnt,last[N];
LL dis[N],w[N],rtd;
struct tree{int l,r,s,fa;LL w;bool rev;}t[N];
struct edge{int to,next;}e[N*2];
std::multiset<LL> se;
std::multiset<LL>::iterator it;

struct Segtree
{
	int rt,sz;
	struct tree{int l,r,s;LL w;}t[N*80];
	
	void ins(int &d,LL l,LL r,LL x,int y)
	{
		if (!d) d=++sz;
		t[d].s+=y;t[d].w+=x*y;
		if (l==r) return;
		LL mid=(l+r)/2;
		if (x<=mid) ins(t[d].l,l,mid,x,y);
		else ins(t[d].r,mid+1,r,x,y);
	}
	
	LL query(int d,LL l,LL r,int k)
	{
		if (t[d].s<=k) return t[d].w;
		if (l==r) return (LL)l*k;
		LL mid=(l+r)/2;
		if (t[t[d].r].s>=k) return query(t[d].r,mid+1,r,k);
		else return t[t[d].r].w+query(t[d].l,l,mid,k-t[t[d].r].s);
	}
}seg;

void addedge(int u,int v)
{
	e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
	e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}

void dfs(int x,int fa,LL d)
{
	d+=w[x];
	if (d>rtd) rtd=d,rt=x;
	for (int i=last[x];i;i=e[i].next)
		if (e[i].to!=fa) dfs(e[i].to,x,d);
}

void get_rt()
{
	dfs(1,0,0);
	int tmp=rt;
	rtd=rt=0;
	dfs(tmp,0,0);
}

bool is_root(int x)
{
	return x!=t[t[x].fa].l&&x!=t[t[x].fa].r;
}

void updata(int d)
{
	t[d].s=t[t[d].l].s+t[t[d].r].s+1;
	t[d].w=t[t[d].l].w+t[t[d].r].w+w[d];
}

void pre(int x,int fa)
{
	int son=0;
	for (int i=last[x];i;i=e[i].next)
	{
		if (e[i].to==fa) continue;
		t[e[i].to].fa=x;
		pre(e[i].to,x);
		if (dis[e[i].to]>dis[son]) son=e[i].to;
	}
	dis[x]=dis[son]+w[x];
	t[x].r=son;
	updata(x);
	for (int i=last[x];i;i=e[i].next)
		if (e[i].to!=fa&&e[i].to!=son) seg.ins(seg.rt,0,inf,dis[e[i].to],1);
}

void remove(int x)
{
	if (!is_root(x)) remove(t[x].fa);
	if (t[x].rev) t[x].rev^=1,t[t[x].l].rev^=1,t[t[x].r].rev^=1,std::swap(t[x].l,t[x].r);
}

void rttl(int x)
{
	int y=t[x].r;
	t[x].r=t[y].l;t[t[y].l].fa=x;
	if (x==t[t[x].fa].l) t[t[x].fa].l=y;
	else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
	t[y].fa=t[x].fa;
	t[y].l=x;t[x].fa=y;
	updata(x);updata(y);
}

void rttr(int x)
{
	int y=t[x].l;
	t[x].l=t[y].r;t[t[y].r].fa=x;
	if (x==t[t[x].fa].l) t[t[x].fa].l=y;
	else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
	t[y].fa=t[x].fa;
	t[y].r=x;t[x].fa=y;
	updata(x);updata(y);
}

void splay(int x)
{
	remove(x);
	while (!is_root(x))
	{
		int p=t[x].fa,g=t[p].fa;
		if (is_root(p))
		{
			if (x==t[p].l) rttr(p);
			else rttl(p);
			return;
		}
		if (x==t[p].l)
			if (p==t[g].l) rttr(g),rttr(p);
			else rttr(p),rttl(g);
		else
			if (p==t[g].r) rttl(g),rttl(p);
			else rttl(p),rttr(g);
	}
}

bool check(int x)
{
	if (x==rt) return 1;
	splay(rt);
	int y=x;
	while (!is_root(y)) y=t[y].fa;
	splay(x);
	return y==rt;
}

int get_rig(int x)
{
	while (t[x].r) x=t[x].r;
	return x;
}

void make_root(int x)
{
	splay(x);t[x].rev^=1;
}

int link(int x,int y)
{
	seg.ins(seg.rt,0,inf,t[x].w,-1);
	seg.ins(seg.rt,0,inf,t[y].w,-1);
	seg.ins(seg.rt,0,inf,t[t[y].r].w,1);
	t[y].r=x;updata(y);
	seg.ins(seg.rt,0,inf,t[y].w,1);
}

void access(int x)
{
	if (check(x)) return;
	while (1)
	{
		splay(x);
		int p=t[x].fa,q;
		if (check(p))
		{
			splay(p);
			if (t[t[p].l].w<t[t[p].r].w) q=get_rig(p),make_root(q),rt=q;
			splay(p);
			if (t[t[p].r].w<t[x].w) link(x,p);
			return;
		}
		splay(p);
		if (t[t[p].r].w>=t[x].w) return;
		link(x,p);
	}
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<n;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		addedge(x,y);
	}
	for (int i=1;i<=n;i++) scanf("%lld",&w[i]),t[i].w=w[i],se.insert(w[i]);
	get_rt();
	pre(rt,0);
	seg.ins(seg.rt,0,inf,dis[rt],1);
	scanf("%d",&q);
	while (q--)
	{
		int op;scanf("%d",&op);
		if (op==0)
		{
			int x,y;scanf("%d%d",&x,&y);
			splay(x);seg.ins(seg.rt,0,inf,t[x].w,-1);
			it=se.lower_bound(w[x]);se.erase(it);
			w[x]+=y;updata(x);seg.ins(seg.rt,0,inf,t[x].w,1);
			se.insert(w[x]);
			access(x);
		}
		else
		{
			int k;scanf("%d",&k);
			if (k==1) it=se.end(),it--,printf("%lld\n",*it);
			else printf("%lld\n",seg.query(seg.rt,0,inf,k-1));
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章