1. 程式人生 > >tarjan求點雙+樹上倍增/圓方樹+並查集--business

tarjan求點雙+樹上倍增/圓方樹+並查集--business

對我沒打錯名字,就是 b u s i n e s s business

題目:

在這裡插入圖片描述
在這裡插入圖片描述

solution:
這道題有很多種寫法,先說我的:
t a r j a n tarjan

求點雙,一個點雙裡的點都可以到達那個最小的點,然後每個割點向他在的點雙連邊建出一棵樹,然後用 S T ST 表一類的樹上倍增方法求解,細節很多,注意有些陣列空間要開大一倍,注意特判 s ,
t s,t
在同一個點的情況

還有一種高階寫法:圓方樹
當然我是沒有寫的了,也是找到點雙然後建圓方樹,可以在圓方樹上倍增,也可以使用並查集把這個樹建成樹高 l o g log 級別的,然後直接跳就行,具體怎麼建就是,將點權看成邊權,從大到小排序,按秩合併,這樣就能保證正確性了

放上我的程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 300005
#define M 600005
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f; 
}
inline int min(int x,int y){return x<y?x:y;}

int n,m,q,cnt,head[N],dfn[N],low[N],stk[N],num,top,tot,root;
int ecnt,ehead[N<<1],val[N<<1],a[N],totn;
int id[N],dep[N<<1],f[N<<1][20],g[N<<1][20];
bool cut[N];
vector<int> dcc[N],bel[N];

struct EDGE{
	int to,nxt;
}edge[M<<1],e[M<<1];
inline void add(int x,int y){
	edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
inline void add2(int x,int y){
	e[++ecnt].to=y; e[ecnt].nxt=ehead[x]; ehead[x]=ecnt;
}

inline void tarjan(int u){
	dfn[u]=low[u]=++num; stk[++top]=u;
	if(u==root && head[u]==0){
		dcc[++tot].push_back(u);
		return;
	}
	int flg=0;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				flg++;
				if(u!=root || flg>1) cut[u]=true;
				++tot; int z; bel[u].push_back(tot);
				do{
					z=stk[top--]; id[z]=tot;
					val[tot]=min(val[tot],a[z]);
					dcc[tot].push_back(z);
					bel[z].push_back(tot);
				}while(z!=v);
				dcc[tot].push_back(u); id[u]=tot;//id[u] 
				val[tot]=min(val[tot],a[u]);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

inline void rebuild(){
	for(int i=1;i<=n;i++)
		if(cut[i]) {
			val[id[i]=((++totn)+tot)]=a[i];
			for(int j=0;j<bel[i].size();j++)
			add2(bel[i][j],id[i]),add2(id[i],bel[i][j]);
		}
}

inline void dfs(int u,int fa){
	for(int i=ehead[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dep[v]=dep[u]+1; f[v][0]=u; g[v][0]=val[v];
		for(int j=1;j<=19;j++)
			f[v][j]=f[f[v][j-1]][j-1],
			g[v][j]=min(g[v][j-1],g[f[v][j-1]][j-1]);
		dfs(v,u);
	} return;
}

inline int get_ans(int x,int y){
	if(x==y) return val[x];
	if(dep[x]<dep[y]) swap(x,y);
	int ans=inf;
	for(int i=19;i>=0;i--)
		if(dep[f[x][i]]>=dep[y]) 
			ans=min(ans,g[x][i]),x=f[x][i];
	if(x==y) return min(ans,val[x]);
	for(int i=19;i>=0;i--)
		if(f[x][i]!=f[y][i])
			ans=min(ans,min(g[x][i],g[y][i])),
			x=f[x][i],y=f[y][i];
	ans=min(min(ans,val[f[x][0]]),min(val[x],val[y]));
	return ans;
}

int main(){
	freopen("business.in","r",stdin);
	freopen("business.out","w",stdout); 
	n=rd(); m=rd(); q=rd();
	for(int i=1;i<=n;i++) a[i]=rd();
	for(int i=1;i<=m;i++){
		int x=rd(),y=rd();
		add(x,y); add(y,x);
	}
	memset(val,0x3f,sizeof val); memset(g,0x3f,sizeof g);
	root=1; tarjan(root);
	rebuild(); totn+=tot; dep[1]=1;
	for(int i=0;i<=19;i++) g[1][i]=val[1];
	dfs(1,0);
	while(q--){
		int s=rd(),t=rd(),x=rd();//注意特判 
		if(s==t) {printf("%lld\n",1LL*a[s]*x);continue;}
		int res=get_ans(id[s],id[t]);
		printf("%lld\n",1LL*res*x);
	}
	return 0;
}
/*
7 9 3
1 2 3 4 5 6 7
1 2
2 5 
1 5
2 3
3 4
2 4
5 6
6 7
5 7
2 3 1
4 6 2
6 7 3
*/
/*
10 20 5
3 4 7 6 8 10 7 4 8 1
10 2
3 7
6 2
7 1
10 7
8 4
3 2
3 9
5 2
2 1
1 6
6 5
8 4
7 4
3 4
5 8
1 8
8 10
8 2
1 1
1 7 9
1 4 2
9 2 4
3 8 1
7 9 1
*/