链接: C - 牛牛的无向图
题意:
一个 n 个点 m 条边的无向图 , 对于每次询问,求出有多少个点对之间的 dis 小于 L , dis定义为 u 能到达 v 的 所有路径中权值最小的路径的权值。
思路:
要是最小路径小于 L ,我们可以对路径大小进行排序,在路径长度小于 L之前的任意两点都是符合的,所以可以用并查集维护连通块的大小 size ,它的贡献就是 C(size , 2) , 同时我们也对 L 进行排序 , 这样就可以依次求出每一个询问。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e6+7;
unsigned int SA, SB, SC; int n, m, q, LIM;
int L[maxn],fa[maxn];
ll cnt[maxn], ans[maxn];
struct node{
int u,v,w;
}e[maxn];
bool cmp(node a,node b){
return a.w < b.w;
}
int find(int x){
if(fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
void conbine(int u,int v){
int temp1 = find(u);
int temp2 = find(v);
if(temp1 != temp2){
cnt[temp1] += cnt[temp2];
}
fa[temp2] = temp1;
}
unsigned int rng61(){
SA ^= SA << 16;
SA ^= SA >> 5;
SA ^= SA << 1;
unsigned int t = SA;
SA = SB;
SB = SC;
SC ^= t ^ SA;
return SC;
}
void gen(){
scanf("%d%d%d%u%u%u%d", &n, &m, &q, &SA, &SB, &SC, &LIM);
for(int i = 1; i <= m; i++){
e[i].u = rng61() % n + 1;
e[i].v = rng61() % n + 1;
e[i].w = rng61() % LIM;
}
for(int i = 1; i <= q; i++){
L[i] = rng61() % LIM;
}
}
int main(){
gen();
sort(e + 1, e + m + 1,cmp);
sort(L + 1,L + q + 1);
for(int i = 0 ; i <= n; i ++){
fa[i] = i;
cnt[i] = 1;
}
ll k = 1,temp = 0;
for(int i = 1; i <= m; i ++){
int u = e[i].u,v = e[i].v,w = e[i].w;
while(w > L[k]){
ans[k++] = temp;
if(k > q) break;
}
if(k > q) break;
int x = find(u),y = find(v);
if(x == y) continue;
temp = temp - cnt[x] * (cnt[x] - 1) / 2 - cnt[y] * (cnt[y] - 1) / 2 + (cnt[x] + cnt[y]) * (cnt[x] + cnt[y] - 1) / 2;
conbine(x,y);
}
for(int i = k; i <= q; i ++){
ans[i] = temp;
}
ll anss = 0;
for(int i = 1; i <= q; i ++) anss = anss ^ ans[i];
printf ("%lld\n",anss);
}