路径统计
题目背景:
11.07 NOIP模拟T2
分析:dfs + tarjan
这种没有思维难度专注于码量和细心程度的题我也是服气······
讲题,我们可以比较轻松的发现,因为每个点一定有出边,并且一共有n条边,那么显然就会形成很多的基环内向树,那么我们可以先tarjan一发统计环上点走到每一个环上其他点的距离之和,将每条边反向,变成基环外向树,然后对于每一个点cur我们dfs它下面挂的那棵树,然后显然它子树中的任意点x到环上其他点的距离为x到cur的距离加上cur到环上那个点的距离,那么我们只需要将x到cur的距离乘上环上的点数再加上cur到环上所有点的距离就可以了。所以到现在思路就比较清晰了,我们先tarjan找到每一个环,然后统计每一个点到其所在环上的其他点的距离和,然后对于每一个点dfs下面的那棵外向树统计贡献就可以了。
(是不是感觉说的很简单,代码不长也就不到4k,呵呵哒)
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
const int MAXN = 500000 + 10;
const int mod = 1000000000 + 7;
struct node {
int to, w;
node(int to = 0, int w = 0) : to(to), w(w) {}
};
std::vector<node> edge[MAXN];
std::vector<int> s;
std::vector<int> sc[MAXN];
int o, n, ind, top;
int t[MAXN], l[MAXN], low[MAXN], num[MAXN], scc[MAXN];
int size[MAXN], d[MAXN], dis[MAXN];
int ans = 0;
long long sum;
bool vis[MAXN];
inline void read_in() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d", &t[i], &l[i]), edge[t[i]].push_back(node(i, l[i]));
}
inline void tarjan(int cur) {
low[cur] = num[cur] = ++ind, s.push_back(cur), vis[cur] = true;
for (int p = 0; p < edge[cur].size(); ++p) {
node *e = &edge[cur][p];
if (num[e->to] == 0)
tarjan(e->to), low[cur] = std::min(low[cur], low[e->to]);
else if (vis[e->to]) low[cur] = std::min(low[cur], num[e->to]);
}
if (low[cur] == num[cur]) {
++top;
do {
o = s.back(), vis[o] = false, s.pop_back(), scc[o] = top;
sc[top].push_back(o);
} while (o != cur);
}
}
inline void add(int &x, int t) {
x += t, (x >= mod) ? (x -= mod) : 0;
}
inline void dfs(int cur) {
if (vis[cur]) return ;
vis[cur] = true, size[cur] = 1, d[cur] = 0;
for (int p = 0; p < edge[cur].size(); ++p) {
node *e = &edge[cur][p];
if (scc[cur] != scc[e->to]) {
dfs(e->to), size[cur] += size[e->to];
add(d[cur], d[e->to]);
add(d[cur], (long long)e->w * size[e->to] % mod);
}
}
long long sz = sc[scc[cur]].size();
add(ans, sz * d[cur] % mod);
add(ans, (long long)(size[cur] - 1) * dis[cur] % mod);
sum += sz * (size[cur] - 1);
}
inline void solve() {
static int temp[MAXN];
for (int i = 1; i <= n; ++i) if (num[i] == 0) tarjan(i);
for (int i = 1; i <= top; ++i) {
if (sc[i].size() == 1) dis[sc[i][0]] = 0;
else {
long long sz = sc[i].size();
int cur = sc[i][0], x = cur;
int sum = 0;
temp[cur] = 0;
do {
temp[t[x]] = (temp[x] + l[x]) % mod, x = t[x];
add(sum, temp[x]);
} while (x != cur);
cur = sc[i][0], x = cur;
dis[cur] = (sum - temp[cur] + mod) % mod;
add(ans, dis[cur]);
do {
dis[t[x]] = (dis[x] + temp[cur]) % mod;
add(dis[t[x]], mod - sz * l[x] % mod);
x = t[x], add(ans, dis[x]);
} while (t[x] != cur);
::sum += sz * (sz - 1LL);
}
}
for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i);
std::cout << (ans - (1LL * n * (n - 1) - sum) % mod + mod) % mod;
}
int main() {
// freopen("road.in", "r", stdin);
// freopen("road.out", "w", stdout);
read_in();
solve();
return 0;
}