GP of Korea Gym103371H Or Machine 最短路

Problem – H – Codeforces

题意:你有一个长为$n$的数组$\{x_i\}$,元素都在$[0, 2^8)$范围内。

你还有一个长为$l$的操作序列,每次操作会有两个数$a,b$,表示$x[a] = x[a] \text{or} x[b]$(按位或)。

现在给定一个数$t$,要求你在操作序列中循环执行操作(如果已经执行完了$l$个则下一次操作从第一个重新开始),总共执行$t$个操作之后,输出得到的序列。

$n,l \le 2^{18} = 262144$,$t \le 10^{18}$。


显然每一位是独立的,所以转换成$8$次只有0和1的问题即可。

我们可以转换一下思路,考虑每个位置是什么时候变成1的。

一个比较显然的想法是对一个操作$a,b$,可以连一条从$b$指向$a$的边,然后跑一遍最短路即可得知每个位置变成1的时间。

但是这样有一些问题,因为$b$之前有可能被修改过,它在同一轮中的不同时刻的值不一定相同。

所以可以把每个点拆成(被修改的次数+1)个点,第一个点是没有被修改的状态。

那么每次操作时先给$a$新建一个点,然后连边(边权是间隔的操作次数),然后从$b$的最新的结点向$a$最新的结点连边(边权也是间隔的操作次数)。

不过注意一个点变成1之后是可以等到下一轮再执行前面的操作的,所以每个点的最后一个点向第一个点补一条边变成一个环(当然要保证每个点的环长度都是$l$)。

每次只有初始距离为0的点不同,跑8次最短路即可。每个点变成1的时间自然就是它那个环的所有点中距离最小的那个。

#include <bits/stdc++.h>
 
using namespace std;
 
constexpr int maxn = 524305;
 
long long d[maxn];
bool vis[maxn];
vector<pair<int, int> > G[maxn];
 
vector<int> vec[maxn];
int u[maxn], v[maxn];
 
int a[maxn], ans[maxn];
 
void solve(int n, int m, int k, long long lim) {
	int s = n + m + 1;
	G[s].clear();
 
	for (int i = 1; i <= n; i++)
		if (a[i] >> k & 1)
			G[s].push_back(make_pair(i, 0));
	
	memset(d, 63, sizeof(d));
	memset(vis, 0, sizeof(vis));
 
	priority_queue<pair<long long, int>, vector<pair<long long, int> >, greater<pair<long long, int> > > heap;
 
	d[s] = 0;
	heap.push(make_pair(0, s));
 
	while (!heap.empty()) {
		int x = heap.top().second;
		heap.pop();
 
		if (vis[x])
			continue;
		
		vis[x] = true;

 
		for (auto pi : G[x]) {
			int y = pi.first, w = pi.second;
 
			if (!vis[y] && d[y] > d[x] + w) {
				d[y] = d[x] + w;

				heap.push(make_pair(d[y], y));
			}
		}
	}
 
	for (int i = 1; i <= n; i++) {
		long long tmp = d[0];
 
		for (auto x : vec[i])
			tmp = min(tmp, d[x]);
 
		if (tmp <= lim)
			ans[i] |= 1 << k;
	}
}
 
int main() {
 
	int n, m;
	long long lim;
 
	scanf("%d%d%lld", &n, &m, &lim);
 
	for (int i = 1; i <= n; i++)
		vec[i].push_back(i);
 
	for (int i = 1; i <= m; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		u[i] = x;
		v[i] = y;
 
		G[vec[x].back()].push_back(make_pair(i + n, (vec[x].back() <= n ? i : i - (vec[x].back() - n))));
		G[vec[y].back()].push_back(make_pair(i + n, (vec[y].back() <= n ? i : i - (vec[y].back() - n))));
		vec[x].push_back(i + n);
	}

	for (int i = 1; i <= n; i++)
		if ((int)vec[i].size() > 1) {
			int t = vec[i].back() - n;

			G[vec[i].back()].push_back(make_pair(i, m - t));
		}
 
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
 
	for (int k = 0; k < 8; k++)
		solve(n, m, k, lim);
	
	for (int i = 1; i <= n; i++)
		printf("%d%c", ans[i], (i < n ? ' ' : '\n'));
 
	return 0;
}

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注