【奇偶最短路 结论】luogu_P7293 Sum of Distances P
题意
给出\(k\)张图\(G_i=(V_i,E_i)\),其中\(|V_i|=n_i,|E_i|=m_i\)。
定义节点\((a_1,a_2,\cdots,a_k)\),其中\(a_i \leq n_i\)。
对于两个节点\((a_1,a_2,\cdots,a_k)\),\((b_1,b_2,\cdots,b_k)\)
如果\(\forall i,(a_i,b_i) \in E_i\),则这两个节点之间连边。
定义\(G\)中位于同一连通分量的两个结点的距离为从一个结点到另一个结点的路径上的最小边数。
计算\(G\)中结点\((1,1,…,1)\)与所有与其在同一连通分量的结点的距离之和,对\(10^9+7\)取模。
思路
可以发现,这个\(k\)维节点的连边相当于同时对\(k\)张图遍历。
当其中一维到达了目标点,它可以反复横跳,等待其它维度遍历完成(前提条件是最短路的奇偶性相同)。
那么先求出奇偶最短路。对于节点\((a_1,a_2,\cdots,a_k)\),从\((1,1,…,1)\)到达它的距离即:\(min(max(odd_i),max(even_i))\)。
然后进行关键一步操作:
对于\(a+b\),显然有这样一个性质:\(a+b=min(a,b)+max(a,b)\)。
将距离的公式代入:\(max(odd_i)+max(even_i)-max(max(odd_i),max(even_i))=min(max(odd_i),max(even_i))\)。
将同一个点的两个值合起来算,又有\(max(odd_i)+max(even_i)-max(max(odd_i,even_i))=min(max(odd_i),max(even_i))\)。
现在反过来考虑,对于一个点\(i\),它对于答案的贡献是多少。
那么对于上述式子的三个\(max\),分开来计算,就是求出其它图中节点小于等于\(max_i\)的个数。
考虑到等于号会有重复情况,那么钦定按图的顺序为第二关键字。
代码
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
const int mod = 1e9 + 7;
int k, m, tot, cnt;
int n[50001], head[100001], ver[400001], next[400001], v[100001], d[100001][3], t[50001];//woshishabi:空间
std::pair<int, int> w[100001];
void add(int u, int v) {
ver[++tot] = v;
next[tot] = head[u];
head[u] = tot;
}
int power(int a, int b) {
int res = 1;
for (; b; b >>= 1) {
if (b & 1)
res = res * a % mod;
a = a * a % mod;
}
return res;
}
int solve(int x) {
memset(t, 0, sizeof(t));
cnt = 0;
for (int i = 1; i <= k; i++, cnt += n[i - 1])
for (int j = 1; j <= n[i]; j++)
w[j + cnt] = std::make_pair(d[j + cnt][x], i);
std::sort(w + 1, w + cnt + 1);
int flag = k, sum = 1, res = 0, tmp1, tmp2;
for (int i = 1; i <= cnt; i++) {
tmp1 = w[i].first, tmp2 = w[i].second;
if (tmp1 > 100000)
break;
if (!t[tmp2]) {
flag--;
if (!flag) {
for (int j = 1; j <= k; j++)
if (j != tmp2)
sum = sum * t[j] % mod;
(res += sum * tmp1 % mod) %= mod;
}
} else if (!flag) {//woshishabi:忘打大括号
sum = sum * power(t[tmp2], mod - 2) % mod;
(res += sum * tmp1 % mod) %= mod;
(sum *= t[tmp2] + 1) %= mod;
}
t[tmp2]++;
}
return res;
}
void bfs(int p) {
std::queue<int> q;
q.push(p);
d[p][0] = 0;
int u, y, even, odd;
while (q.size()) {
u = q.front(), even = d[u][0], odd = d[u][1];
q.pop();
for (int i = head[u]; i; i = next[i]) {
y = ver[i];
int flag = 0;
if (odd + 1 < d[y][0])
d[y][0] = odd + 1, flag = 1;
if (even + 1 < d[y][1])
d[y][1] = even + 1, flag = 1;
d[y][2] = std::max(d[y][0], d[y][1]);
if (flag)
q.push(y);
}
}
}
signed main() {
scanf("%lld", &k);//woshishabi:输入没用%lld
for (int i = 1; i <= 100000; i++)
d[i][0] = d[i][1] = d[i][2] = 200000;
for (int i = 1; i <= k; i++) {
scanf("%lld %lld", &n[i], &m);
for (int j = 1, x, y; j <= m; j++) {
scanf("%lld %lld", &x, &y);
x += cnt, y += cnt;
add(x, y), add(y, x);
}
bfs(cnt + 1);
cnt += n[i];
}
printf("%lld", ((solve(0) + solve(1) - solve(2)) % mod + mod) % mod);
}

浙公网安备 33010602011771号