「JOISC 2019 Day3」指定城市 Solution

「JOISC 2019 Day3」指定城市

题意

\(~~~~\) 给一棵 \(n\) 个点的树,每条边双向分别有一个权值。现给出 \(q\) 个询问,每个询问可以指定 \(e_i\) 个点为特殊点,对每个特殊点所有非该特殊点到该点的路径相同方向上的边免费,求最小代价。

\(~~~~\) \(1\leq n\leq 2\times 10^5,q\leq n\)

题解

\(~~~~\) 似乎不难,但我打了3k,/kk

\(~~~~\) 首先由子任务外加手玩不难发现 \(e=1\) 的时候应该做法不大相同,可以发现此时如果把选择的城市作为根,那么最终的答案就是所有向下的边的权值之和,直接换根DP即可,具体实现可见代码。

\(~~~~\) 再看 \(e=2\) ,此时若在某个点 \(u\) 的子树内选两个不属于同一儿子的结点 \(v_1,v_2\),则节省的代价一定是 \(u\) 作为根时节省的代价加上 \(u\) 到这两个点节省的代价,在上面换根DP二次扫描时取最大值即可。

\(~~~~\) 否则当 \(e>2\) 时,感性认识应该是较之前 \(e-1\) 时选择的点不会改变,只会增加。故每次贪心选取节省的代价最大的那个即可,然后删去它造成负贡献的那些边。不难想到将原树以 \(e=2\) 时其中一个选择的点为根跑一遍dfs序,这样就可以用线段树边贡献。由于每条边最多删一次,所以这一部分复杂度是 \(\mathcal{O(n \log n)}\).

代码

查看代码
#include <cstdio>
#include <vector>
#include <algorithm>
#define ll long long
#define PII pair<ll,ll>
#define mp(a,b) make_pair(a,b)
using namespace std;
vector < pair<int,PII> > G[200005];
int root,n,q,fa[200005],pos1,pos2;
ll tot,dp[200005],Ans[200005],dis[200005],pos[200005];
void dfs1(int u,int Fa)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].first;
		if(v==Fa) continue;
		dis[v]=dis[u]+G[u][i].second.first;
		dfs1(v,u);dp[u]+=dp[v]+G[u][i].second.second;
	}
}
void dfs2(int u,int Fa)
{
	pos[u]=u;Ans[1]=max(Ans[1],dp[u]);root=(Ans[1]==dp[u]?root:u);
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].first;
		if(v==Fa) continue;
		dp[u]-=dp[v]+G[u][i].second.second;
		dp[v]+=dp[u]+G[u][i].second.first;
		dfs2(v,u);
		dp[v]-=dp[u]+G[u][i].second.first;
		dp[u]+=dp[v]+G[u][i].second.second;
		if(dp[u]+dis[pos[u]]+dis[pos[v]]-dis[u]*2>Ans[2])
		{
			Ans[2]=dp[u]+dis[pos[u]]+dis[pos[v]]-dis[u]*2;
			pos1=pos[u]; pos2=pos[v];
		}
		if(dis[pos[u]]<dis[pos[v]]) pos[u]=pos[v];
	}
}
void Solve1()
{
	dfs1(1,0);
	dfs2(1,0);
}
bool On[200005];
bool changed[200005];
int dfn[200005],Times,L[200005],R[200005],Val[200005];
struct SegmentTree{
	#define ls p<<1
	#define rs p<<1|1
	#define lson p<<1,l,mid
	#define rson p<<1|1,mid+1,r 
	ll tr[800005],tag[800005],AnsPos[800005];
	void pushUp(int p)
	{
		tr[p]=max(tr[ls],tr[rs]);
		if(tr[p]==tr[ls]) AnsPos[p]=AnsPos[ls];
		if(tr[p]==tr[rs]) AnsPos[p]=AnsPos[rs];
	}
	void pushDown(int p)
	{
		if(tag[p])
		{
			tr[ls]+=tag[p]; tr[rs]+=tag[p];
			tag[ls]+=tag[p]; tag[rs]+=tag[p];
			tag[p]=0;
			return;
		}
	}
	void Build(int p,int l,int r)
	{
		if(l==r)
		{
			tr[p]=0;
			AnsPos[p]=l;
			return;
		}
		int mid=(l+r)>>1;
		Build(lson); Build(rson);
		pushUp(p);
	}
	void Modify(int p,int l,int r,int lx,int rx,int val)
	{
		if(lx<=l&&r<=rx)
		{
			tr[p]+=val;
			tag[p]+=val;
			return;
		}
		int mid=(l+r)>>1;pushDown(p);
		if(lx<=mid) Modify(lson,lx,rx,val);
		if(mid<rx)  Modify(rson,lx,rx,val);
		pushUp(p);
	}
	inline PII Query(){return mp(AnsPos[1],tr[1]);}
}Seg;
bool Tag(int u,int Fa)
{
	if(u==pos2) {On[u]=true;return true;}
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].first;
		if(v==Fa) continue;
		if(Tag(v,u)) {On[u]=true;return true;}
	}
	return false;
}
int To[200005];
void dfs4(int u,int Fa)
{
	ll Sum=0;
	dfn[u]=++Times;fa[u]=Fa;
	L[u]=Times;To[Times]=u;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].first;
		if(v==Fa) continue;
		dfs4(v,u);Val[v]=G[u][i].second.first;
		if(!On[v]) Seg.Modify(1,1,n,L[v],R[v],G[u][i].second.first),Ans[2]+=G[u][i].second.first;
	}
	R[u]=Times;
}
void Solve2(){Tag(pos1,0);dfs4(pos1,0);}
void Solve()
{
	for(int i=3;i<=n;i++)
	{
		PII P=Seg.Query();
		Ans[i]=Ans[i-1]-P.second;
		int x=To[P.first];
		while(!On[x]&&!changed[x])
		{
			Seg.Modify(1,1,n,L[x],R[x],-Val[x]);
			changed[x]=true;x=fa[x];
		}
	}
}
int main() {
	scanf("%d",&n);
	for(int i=1,u,v,a,b;i<n;i++)
	{
		scanf("%d %d %d %d",&u,&v,&a,&b);
		G[u].push_back(mp(v,mp(a,b)));
		G[v].push_back(mp(u,mp(b,a)));tot+=a+b;
	}
	Seg.Build(1,1,n);
	Solve1();Ans[1]=tot-Ans[1];Ans[2]=0;
	Solve2();
	Solve();
	scanf("%d",&q);
	while(q--)
	{
		int x;scanf("%d",&x);
		printf("%lld\n",Ans[x]);
	}
	return 0;
}
posted @ 2022-01-29 09:09  Azazеl  阅读(90)  评论(0)    收藏  举报