H. Life is a Game 2021ICPC上海 (kruskal重构树+倍增)

题目链接

题意

⼀张带边权带点权⽆向图。从某点出发,有初始声望。每第⼀次到达⼀个点将获得点权等值的声望加成。经过⼀条边需要满⾜边权等值的最低声望限制。多次给出起点和初始声望,询问能达到的最⼤声望。

思路

对于图上路径边权值的限制,可以往kruskal重构树方向思考。构建出kruskal重构树就可以把边权转为点权,若求两点之间路径的限制,只需求lca的点权,这里可以和lca一样倍增向上跳。由于kruskal重构树中深度越深的点点权(原边权)越小,所以若能到达v,则以v为根的子树都可以到达。预处理出子树点权和后利用树上倍增就可以做到单词询问log的复杂度。

代码

// Problem: H. Life is a Game
// Contest: Codeforces - The 2021 ICPC Asia Shanghai Regional Programming Contest
// URL: https://codeforces.com/gym/103446/problem/H
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// Time: 2022-11-03 21:27:46
// 
// Powered by CP Editor (https://cpeditor.org)

//fw
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<unordered_map>
#include<stack>
#include<cmath>
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
#define debug(a) cout<<#a<<"="<<a<<endl;
#define sv(a,l,r,x) for(int i=l;i<=r;i++)a[i]=x;
#define pii pair <int, int>
#define endl '\n'
#define pb push_back
#define lc u<<1
#define rc u<<1|1
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+10,M=2*N;
int h[N],e[M],ne[M],idx;
void add(int a,int b)
{
	e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
struct node
{
	int u,v,w;
}edge[N];
int n,m,q,p[N],fa[N][20],cnt;
ll maxp[N];
ll a[N];
int find(int x)
{
	return p[x]==x?x:p[x]=find(p[x]);
}
bool cmp(node a,node b)
{
	return a.w<b.w;
}
int kruskal()
{
	sort(edge+1,edge+1+m,cmp);
	cnt=n;
   for(int i=1;i<=n;i++)p[i]=i;

	for(int i=1;i<=m;i++)
	{
		int  u=edge[i].u,v=edge[i].v,w=edge[i].w;
		u=find(u);v=find(v);
		if(u!=v)
		{
			//建立重构树
			maxp[++cnt]=w;
			p[cnt]=p[u]=p[v]=cnt;
			add(u,cnt);add(cnt,u);
			add(v,cnt);add(cnt,v);
		}
	}
	return cnt;
}
void dfs(int u,int ffa)
{
	fa[u][0]=ffa;
	for(int i=1;i<=19;i++)fa[u][i]=fa[fa[u][i-1]][i-1];//倍增能跳到的点
	for(int i=h[u];~i;i=ne[i])
	{
		int ver=e[i];
		if(ver==ffa)continue;
		dfs(ver,u);
		a[u]+=a[ver];
	}
}
int main()
{
	IOS
   cin>>n>>m>>q;
   memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)cin>>a[i];
   for(int i=1;i<=m;i++)
   {
   	int a,b,c;
   	cin>>a>>b>>c;
   	edge[i]={a,b,c};
   }   	

   n=kruskal();
//   cout<<n<<endl;
   dfs(n,0);
   maxp[0]=1e18;//防止跳过头...
   while(q--)
   {
   	int x,k;
   	cin>>x>>k;
   	ll ans=a[x]+k;
   	while(x!=n)//有些点可能需要更新ans后再跳
   	{
   		int temp=x;
   		for(int i=19;i>=0;i--)
   			if(maxp[fa[x][i]]<=ans)
   				x=fa[x][i];

   		if(temp==x)break;//如果跳不动了
   		
   		ans=a[x]+k;
   	}
   	cout<<ans<<endl;
   }
    return 0;
}
posted @ 2022-11-03 22:31  Avarice_Zhao  阅读(33)  评论(0编辑  收藏  举报