正睿 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
可能不会补了。

浙公网安备 33010602011771号