2026.3.2 NOI 模拟赛 题解
T1 XOR and Min-cut
题意
一张 \(n\) 点的图,给定 \(s,t\),\(m\) 次操作每次加入一条无向边,每次操作后求出 \(\min_{S\subset V,s\in S,t\notin S} \bigoplus_{(u,v,w)\in E,u\in S,v\notin S} w\),多测 \(\sum n,\sum m\le10^6\),\(w\le2^{60}\),可能有重边,不存在自环
分析
对于固定的边集,先令 \(S=\{s\}\),此时权值为 \(\bigoplus_{(s,v,w)\in E}\)
更改一个点的状态(在 \(S\) 中或不在 \(S\) 中),则权值异或上其所有邻点的边权
令 \(W_u=\bigoplus_{(u,v,w)\in E} w\),则答案为 \(\min_{S\subset V,s\in S,t\notin S} \bigoplus_{u\in S}W_u\),线性基即可
考虑原问题,每次加入一条边修改 \(O(1)\) 个 \(W\)
转化为 \(O(n+m)\) 个数值每个在线性基中存在一段时间,每个时刻求最小值
容易做到 \(O(\sum (n+m)\log w)\)
代码:
#include <bits/stdc++.h>
using namespace std;
int n, m, s, t;
long long wt[1000010];
int p[1000010];
vector<pair<int, long long> > md[1000010];
struct linb {
long long v[62];
int t[62];
void ins(long long x, int tm){
for (int i = 59; ~i; --i)if (x >> i & 1){
if (!v[i]){v[i] = x;t[i] = tm;return;}
if (tm > t[i])swap(tm, t[i]), swap(v[i], x);
x ^= v[i];
}
}
long long min_with(long long x, int l) const {
for (int i = 59; ~i; --i)if (t[i] >= l)x = min(x, x ^ v[i]);
return x;
}
};
long long ww[1000010];
void work(){
cin >> n >> m >> s >> t;
fill_n(wt + 1, n, 0);
fill_n(p + 1, n, 1);
for (int i = 1; i <= m; ++i)md[i].clear();
auto psh = [&](int l, int r, long long w){if (l <= r && w)md[l].emplace_back(r, w);};
for (int i = 1; i <= m; ++i){
int u, v; long long w;
cin >> u >> v >> w;
if (u != v){
auto del = [&](int u){if (u != s && u != t)psh(p[u], i - 1, wt[u]);};
auto ins = [&](int u){if (u != s && u != t)p[u] = i;};
del(u);wt[u] ^= w;ins(u); del(v);wt[v] ^= w;ins(v);
}
ww[i] = wt[s];
}
for (int i = 1; i <= n; ++i)if (i != s && i != t)psh(p[i], m, wt[i]);
linb ln{};
for (int i = 1; i <= m; ++i){
for (auto [j, w] : md[i])ln.ins(w, j);
cout << ln.min_with(ww[i], i) << "\n";
}
}
int main(){
freopen("mincut.in", "r", stdin);
freopen("mincut.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)work();
return 0;
}
T2 \(\textcolor{black}\odot\) AT_agc034_f [AGC034F] RNG and XOR
题意
一个值初始为 \(0\),每次异或上 \([0,2^n)\) 中的一个数,选择 \(i\) 的概率为 \(\frac{p_i}{\sum p_i}\),对于每个 \(0\le i<2^n\) 求出第一次得到 \(i\) 的期望操作次数,\(n\le20\)
分析
先将 \(p\) 归一化
令 \(f_i\) 表示 \(i\) 的答案,则
其中 \(\ast\) 表示异或卷积
由于 \(\sum p_i=1\),显然
从而
即 \(p\ast f=f+V\),其中 \(V_0=2^n-1\),\(V_{\ne 0}=-1\)
令 \(p'_i=p_i-[i=0]\),则 \(p'\ast f=V\)
\(V\) 和 \(p'\) 都是容易求的,容易通过 \(\text{FWT}\) 求出 \(f\)
总时间复杂度 \(O(n2^n)\)
T3 \(\textcolor{black}\odot\) P12478 [集训队互测 2024] Désive
题意
给定 \(a_{1\sim 2^n}\in[0,2^n)\),\(q\) 次询问,每次给定 \(l_1,r_1,l_2,r_2\),查询 \(\sum_{l_1\le l\le r_1}\sum_{l_2\le r\le r_2}f(l,r)\),其中 \(f(l,r)=\max_x \text{mex}_{i=l}^r (a_i\oplus x)\),\(n\le 18,q\le 10^6\)
分析
对于一个 \(f(l,r)\),将 \(a_{l\sim r}\) 加入一颗 \(\text{Trie}\) 中,在树上 \(dp\)
若一个叶子存在,则 \(dp\) 值为 \(1\),若一个结点左右子树中至少一个是满的则当前结点的 \(dp\) 值为左右子树之和,否则为左右子树的较大值,答案为根的 \(dp\) 值
暴力实现容易做到 \(O(4^nn+q)\)
将询问离线,差分并挂在右端点上,扫描线,则变为类似历史值之和的结构,考虑如何维护 \(f\)
前面暴力计算的过程,可以视为选择一个叶子删去它到根的链(不含它本身),答案为剩余的所有满的子树的大小之和
考虑 \(a\) 为排列的情况
右端点扫描过程中,对于每个叶子结点维护一个分段函数,自变量为左端点,函数值表示取当前叶子时对应区间的 \(f(l,r)\),显然对于一个右端点,每个叶子结点的分段函数至多 \(O(n)\) 段,对它 \(O(n)\) 个祖先都有贡献,总段数为 \(O(n^22^n)\)
实际上每个子树内部归并可以压缩到 \(O(n2^n)\)
每一段可以通过一次区间取 \(\max\) 实现,因此 \(O(n2^n)\) 次区间 \(\max\),并维护历史和,可以做到 \(O(n^22^n+nq)\)
然后考虑一般情况
插入一个右端点后,若一个子树内满的最大左端点从 \(t'\) 变为 \(t\),则需要考虑当前子树在 \((t',t]\) 内的分段函数的贡献
每个子树维护一个 \(\text{Trie}\),用开始时的方法,每次不断删去 \((t',t]\) 内的元素即可得到对应区间的分段
可以做到 \(O(n^22^n+nq)\)
比赛结果
\(100+16+35\),\(\text{rk}3\)

浙公网安备 33010602011771号