G. Vlad and the Mountains codeforces Round 888 (Div. 3) 1851G

题目大意:有一个n个点的无向图,每个点有点权wi,如果要从点u移动到v,需要消耗w[v]-w[u]的能量,一共q次询问,每次给e点初始能量,问能否从a走到b

2<=n<=2e5;1<=q<=1e5

思路:我们发现,如果要从u走到k走到v,那么消耗的能量为w[u]-w[k]+w[k]-w[v],也就等于w[u]-w[v],所以要想从a走到b,只要存在连通两点的一条路径满足w[u]+e>=所有路径上的点即可,那么我们将每个边都存成从w更大的点到w更小的点的单向边,同时按照更大的点权排序,这样对于某一个询问,我们将所有最大点权小于等于w[a]+e的边连通如果a和b连通了,那么a就可以到b,否则说明他们中间有点权>w[a]+e的点,维护连通性可以用并查集(但要注意路径压缩,否则会超时),然后对于所有查询,我们可以将他们按w[a]+e排序,从小到大遍历,依次连通点券更小的边即可在O(n)内完成

//#include<__msvc_all_public_headers.hpp>
#include
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
const ll MOD=998244353;
ll a[N];
pair>e[N];
pair,int>>qu[N];
int fa[N];
bool ans[N];
vectorg[N];
int lv[N];
bool vis[N];
int find(int x)
{//并查集找祖先
	return fa[x] == x ? x : find(fa[x]);
}
void unite(int a, int b)
{//并查集的合并
	int x = find(a);
	int y = find(b);
	if (lv[x] < lv[y])//路径压缩
		swap(x, y);
	fa[y] = x;
	if (lv[x] == lv[y])
	{
		lv[x]++;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while (t--)
	{
		int n, m;
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
		{//初始化
			vis[i] = 0;
			lv[i] = 0;
			g[i].clear();
			fa[i] = i;
			cin >> a[i];
		}
		for (int i = 1; i <= m; i++)
		{
			int u, v;
			cin >> u >> v;
			e[i].first = max(a[u],a[v]);//两点点权的最大值,用于排序
			if (a[u] > a[v])
			{
				g[u].push_back(v);//按点权从大到小渐变,便于遍历
				e[i].second = make_pair(u, v);
			}
			else
			{
				g[v].push_back(u);
				e[i].second = make_pair(v, u);
			}
		}
		int q;
		cin >> q;
		for (int i = 1; i <= q; i++)
		{
			ans[i] = 0;
			ll x;
			cin >> qu[i].second.first.first >> qu[i].second.first.second >> x;//记录a,b
			qu[i].first = x + a[qu[i].second.first.first];//记录w[a]+e
			qu[i].second.second = i;//记录询问的编号
		}
		sort(e + 1, e + m + 1);
		sort(qu + 1, qu + q + 1);
		int itj = 1;
		for (int i = 1; i <= q; i++)
		{
			ll now = qu[i].first;
			while (itj <= m && e[itj].first <= now)
			{//遍历所有最大点权<=e[a]+b的边
				int u = e[itj].second.first;
				if(!vis[u])
				{//连通过的不用再访问了
					vis[u] = 1;
					for (int i = 0; i < g[u].size(); i++)
					{//所有点权小于该点的点都可以被放入连通图内						
						int v = g[u][i];
						unite(u, v);
					}
				}
				itj++;
			}
			int u = find(qu[i].second.first.first);
			int v = find(qu[i].second.first.second);
			ans[qu[i].second.second] = u == v ? 1 : 0;//a,b当前连通,说明存在中间没有w[a]+e的点的路径
		}
		for (int i = 1; i <= q; i++)
		{
			cout << (ans[i] ? "YES" : "NO") << endl;
		}
	}
	return 0;
}

你可能感兴趣的:(图论,算法,c++)