[APIO2020]交换城市(交互+kruskal重构树)

[APIO2020]交换城市

description

solution

在这里插入图片描述

如果 u , v u,v u,v存在于一条链上(只有两个点度数为 1 1 1其余点度数为 2 2 2)则无解,否则必有解

如图,不管是哪个点度数 > 2 >2 >2,都可以有解

以蓝色为例,第二个红点右移到蓝色右边,第一个红点走进蓝色,第二个红点走到第一个红点开始位置,第一个红点出来回到第二个红点开始位置

因此本题只是在kruskal重构树(每次新建一个点,把两个点连在新点下面当儿子,新点点权即为原两点的边权)的基础上多了一个链判断的维护

不需要用启发式合并,只需要判断siz[u]=cnt[u][1]+cnt[u][2]&&cnt[u][1]==2

code

#include "swap.h"
#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
struct node  {
	int u, v, w;
	node(){}
	node( int U, int V, int W ) {
		u = U, v = V, w = W;
	}
}edge[maxn];
vector < int > G[maxn];
int n, m, ip;
int MS[maxn], d[maxn], val[maxn], siz[maxn], g[maxn], dep[maxn];
int f[maxn][20], cnt[maxn][3];

bool cmp ( node x, node y ) {
	return x.w < y.w;
}

int find( int x ) {
	return x == MS[x] ? x : MS[x] = find( MS[x] );
}

void dfs( int u, int fa ) {
	f[u][0] = fa, dep[u] = dep[fa] + 1;
	for( int i = 1;i < 20;i ++ )
		f[u][i] = f[f[u][i - 1]][i - 1];
	if( cnt[u][1] == 2 && cnt[u][1] + cnt[u][2] == siz[u] ) g[u] = g[fa];
	else g[u] = u;
	for( int i = 0;i < ( int )G[u].size();i ++ ) {
		int v = G[u][i];
		if( v == fa ) continue;
		else dfs( v, u );
	}
}

void modify( int x, int v ) {
	if( 1 <= d[x] && d[x] <= 2 ) cnt[find( x )][d[x]] += v;
}

void kruskal() {
	sort( edge + 1, edge + m + 1, cmp );
	for( int i = 1;i <= n;i ++ ) MS[i] = i, siz[i] = 1;
	ip = n;
	for( int i = 1;i <= m;i ++ ) {
		int u = edge[i].u, v = edge[i].v, w = edge[i].w;
		int fu = find( u ), fv = find( v );
		if( fu ^ fv ) {
			++ ip;
			cnt[ip][1] = cnt[fu][1] + cnt[fv][1];
			cnt[ip][2] = cnt[fu][2] + cnt[fv][2];
			siz[ip] = siz[fu] + siz[fv];
			MS[fu] = ip, MS[fv] = ip;
			val[ip] = w;
			MS[ip] = ip;
			G[ip].push_back( fu );
			G[ip].push_back( fv );
		}
		else if( val[fu] != w ) {
			++ ip;
			cnt[ip][1] = cnt[fu][1];
			cnt[ip][2] = cnt[fu][2];
			siz[ip] = siz[fu];
			MS[fu] = ip;
			MS[ip] = ip;
			val[ip] = w;
			G[ip].push_back( fu );
		}
		modify( u, -1 );
		modify( v, -1 );
		d[u] ++;
		d[v] ++;
		modify( u, 1 );
		modify( v, 1 );
	}
	dfs( ip, 0 );
}

int get_lca( int u, int v ) {
	if( dep[u] < dep[v] ) swap( u, v );
	for( int i = 19;~ i;i -- )
		if( dep[f[u][i]] >= dep[v] )
			u = f[u][i];
	if( u == v ) return u;
	for( int i = 19;~ i;i -- )
		if( f[u][i] != f[v][i] )
			u = f[u][i], v = f[v][i];
	return f[u][0];
}

void init( int N, int M, vector < int > U, vector < int > V, vector < int > W ) {
	n = N, m = M;
	for( int i = 0;i < m;i ++ )
		edge[i + 1] = node( U[i] + 1, V[i] + 1, W[i] );	
	kruskal();
}

int getMinimumFuelCapacity( int x, int y ) {
	x ++, y ++;
	int lca = get_lca( x, y );
	if( ! g[lca] ) return -1;
	else return val[g[lca]];
}

完全不能调试的交互题!!!
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值