2018 GCPC ( MST + 树上路径 )

Mountaineers

解题报告:
最大值最小的路径肯定在MST上面,那么我们直接求一个MST然后就是任意两点之间的树上路径最小值即可。
树上倍增即可。但是要注意同一个点的情况。

#include<bits/stdc++.h>
#define LL  long long
#define pii pair<LL,int>
#define all(x) x.begin(),x.end()
#define wp(x) write(x),putchar('\n')
#define wpl(x) write(x),putchar(' ')
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn = 500 + 5;
const int MOD = 998244353;

inline int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {s = (s << 1) + (s << 3) + ch - '0'; ch = getchar();}
	return s * f;
}
inline void write(LL x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
struct node{
	int x,y,w;
	node(){}
	node(int _x,int _y,int _w){x=_x;y=_y;w=_w;}
	bool operator < (const node &ls) const {return ls.w>w;}
}p[maxn*maxn<<2];
struct TMD{
	int to,next,w;
	TMD(){}
	TMD(int _to,int _next,int _w){to=_to,next=_next;w=_w;}
}edge[maxn*maxn<<2];
int head[maxn*maxn],knt,pre[maxn*maxn][25],Max[maxn*maxn][25],deep[maxn*maxn];
int f[maxn*maxn],mp[maxn][maxn];
void add(int from,int to,int w) {edge[++knt]={to,head[from],w};head[from]=knt;}
int getfind(int x) {return x==f[x]?x:f[x]=getfind(f[x]);}
void dfs(int now,int fa){
	deep[now]=deep[fa]+1;pre[now][0]=fa;
	for(int i=1;i<20;i++){
		pre[now][i]=pre[pre[now][i-1]][i-1];
		Max[now][i]=max(Max[now][i-1],Max[pre[now][i-1]][i-1]);
	}
	for(int i=head[now];i;i=edge[i].next){
		int to=edge[i].to,w=edge[i].w;
		if(to==fa) continue;
		Max[to][0]=w;
		dfs(to,now);
	}
}
int query(int x,int y){
	int ans=0;
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=19;i>=0;i--){
		if(deep[pre[x][i]]>=deep[y]){
			ans=max(ans,Max[x][i]);
			x=pre[x][i];
		}
	}
	if(x==y) return ans;
	for(int i=19;i>=0;i--){
		if(pre[x][i]!=pre[y][i]){
			ans=max(ans,max(Max[x][i],Max[y][i]));
			x=pre[x][i];y=pre[y][i];
		}
	}
	ans=max(ans,max(Max[y][0],Max[x][0]));
	return ans;
}
int main() {
	int n=read(),m=read(),q=read(),cnt=0,x1,y1,x2,y2,tp1,tp2;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) mp[i][j]=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(i<n) p[++cnt]=node((i-1)*m+j,i*m+j,max(mp[i][j],mp[i+1][j]));
			if(j<m) p[++cnt]=node((i-1)*m+j,(i-1)*m+j+1,max(mp[i][j],mp[i][j+1]));
		}
	}
	sort(p+1,p+cnt+1);
	for(int i=1;i<=n*m;i++) f[i]=i;
	int sum=1,k1,k2;
	for(int i=1;i<=cnt;i++){
		k1=getfind(p[i].x);k2=getfind(p[i].y);
		if(k1!=k2){
			f[k2]=k1;sum++;
			add(p[i].x,p[i].y,p[i].w);
			add(p[i].y,p[i].x,p[i].w);
		}
		if(sum==n*m) break;
	}
	dfs(1,0);
	for(int i=1;i<=q;i++){
		x1=read(),y1=read();x2=read(),y2=read();
		tp1=(x1-1)*m+y1;tp2=(x2-1)*m+y2;
		wp(max(query(tp1,tp2),max(mp[x1][y1],mp[x2][y2])));
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值