正睿 2025 NOIP20 连测 Day5 做题记录

T1

\(m\) 个质数,第 \(i\) 个质数 \(p_i\) 出现了 \(n_i\) 次。求一种划分质数的方案,使得第一个集合的和等于第二个集合的乘积。

萌萌题,注意到最后相当于是要求 \(p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_k^{\alpha_k}+\alpha_1p_1+\alpha_2p_2+\cdots+\alpha_kp_k=\sum n_ip_i\),发现 \(\sum n_ip_i-\sum \alpha_ip_i\) 的可能的取值范围只有 \([sum-\log V, V]\),直接枚举这个数然后质因数分解即可。

T2

给出 \(m\) 个限制,限制形如 \(x_a-x_b=c\pmod d\),问每个限制是否与之前的限制冲突。

这个题比较神,考场上没想出来。可以开 \(\log V\) 个带权并查集,第 \(i\) 个并查集上两个点 \(a,b\) 之间的距离代表 \(x_a-x_b \bmod 2^i\),来一个新限制的时候先检查是否与之前的限制重复了,如果重复就判断是否合法,不重复就直接添加。

这个的正确性可以这么想:\(d\) 大的限制会给更小的 \(d'<d\) 另一个限制 \((c\bmod d', d')\),然后一个新的限制 \((c,d)\) 只要满足所有 \(d'<d\)\((c',d')\) 的限制就会合法。

考场上想要把这个东西离线下来建出树来做树剖,但是发现每个位上的树形态可能不一样,遂倒闭。

const int MAXN = 5e5 + 5, MAXV = 32;
int n, m;

struct _dsu {
	int fa[MAXN], dis[MAXN];

	void merge(int x, int y, int w) {
		fa[x] = y;
		dis[x] = w;
	}

	int find(int x) {
		if (fa[x] == x) return x;
		int f = find(fa[x]);
		dis[x] += dis[fa[x]];
		fa[x] = f;
		return f;
	}

	void init() {
		for (int i = 1; i <= n; ++i)
			fa[i] = i;
	}
} dsu[MAXV + 1];

int f(int x, int d) {
	return (x % (1ll << d) + (1ll << d)) % (1ll << d);
}

void work() {
	cin >> n >> m;
	for (int i = 0; i <= MAXV; ++i)
		dsu[i].init();
	for (int i = 1; i <= m; ++i) {
		int a, b, c, d;
		cin >> a >> b >> c >> d;
		if (d == -1) d = MAXV;
		else {
			if (d == 1) d = 0;
			else d = __lg(d);
		}
		bool flg = true;
		for (int i = 0; i <= d; ++i) {
			if (dsu[i].find(a) != dsu[i].find(b)) continue;
			if (f(dsu[i].dis[a] - dsu[i].dis[b], i) == f(c, i))
				continue;
			flg = false; break;
		}
		if (flg == false) {
			cout << 0 << endl;
			continue;
		}
		for (int i = 0; i <= d; ++i) {
			if (dsu[i].find(a) == dsu[i].find(b)) continue;
			int da = dsu[i].dis[a], db = dsu[i].dis[b];
			dsu[i].merge(dsu[i].find(a), dsu[i].find(b), f(c - da + db, i));
		}
		cout << 1 << endl;
	}
}

T3/T4

可能不会补了。

posted @ 2025-10-20 21:38  小蛐蛐awa  阅读(16)  评论(0)    收藏  举报