P5994 [PA 2014] Kuglarz 题解
P5994 [PA 2014] Kuglarz 题解
P5994 [PA 2014] Kuglarz - 洛谷
确实是一道非常深刻的题目。
感觉其他题解都没有讲出要点,或者讲明白我的疑点,于是记录下来,希望有所帮助。
题解主要说明了,如何想到建图,以及如何把区间询问问题转化为前缀问题,再转化成最小生成树的问题。
先来看这个问题
你有一个序列
同时给出 yes 或 no。
我们考虑把给出的区间和转化为前缀和。设
我们将相互可以推出的
比如给出下面三个区间:
1 2
2 3
2 2
我们可以得到这样的图:
当知道了
这段参考了 (前缀和/并查集)代码源每日一题 Div1 区间和 - 知乎,并对题面进行了一点修改。
问题中的操作和建图有什么关系
看完前面的题目,好像和这道题目并没有什么关系。但我们发现,询问奇偶性的操作其实可以看作一段区间的异或值。也就是在下文中,奇偶性和异或和实际上是等价的。
设
这里就解释了我不理解的第一个问题,为什么区间询问问题可以转化为前缀询问?
联系到上一题上,发现区间异或和操作可以转化为前缀异或和操作,令
我们考虑对于
发现询问区间
以及第二个问题,为什么需要最小生成树?
由于只关心是否相同,所以两式子同时
换句话来说,想知道
由上一题我们已经知道,这样连边之后,对于同一连通块内的点可以相互求出值,也就是我们希望能把
于是问题就转化完成了,即:
给定
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct note{
int u,v,w;
}edg[4000010];
bool cmp(note x,note y) {return x.w < y.w;}
int fa[2010];
int getf(int x)
{
if(fa[x] == x) return x;
return fa[x] = getf(fa[x]);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n;
cin >> n;
int cnt = 0;
for(int i = 1;i <= n;i++)
for(int j = i;j <= n;j++)
{
int w;cin >> w;
edg[++cnt] = {i - 1,j,w};
}
sort(edg + 1,edg + cnt + 1,cmp);
int ans = 0;
for(int i = 1;i <= n;i++) fa[i] = i;
for(int i = 1;i <= cnt;i++)
{
int u = edg[i].u,v = edg[i].v,w = edg[i].w;
u = getf(u),v = getf(v);
if(u == v) continue;
fa[u] = v;
ans += w;
}
cout << ans;
return 0;
}
使用了 kruskal,可以修改成 prim。
希望对你有所帮助。