2017.11.8 T2
这道题坑了我一天的时间,下次我要记住合理的安排时间,T3 其实非常简单的,在T2上浪费了太多的时间。
讲一下思路,先找到环,由于图中每个点只有一条出边,而且每个点都作为一条边的起点,所以,环只有一种且环一定是图的尾部。那我们先 Tarjan 找到环,建反向边,用反向边查找环上每个节点向下的子树,对于子树中的边的贡献,也就是它会被经过的次数 :
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
inline int read() {
int i = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') f = -1; ch = getchar();
}
while(isdigit(ch)) {
i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
}
return i * f;
}
const int MAXN = 5e5 + 5;
const int mod = 1e9 + 7;
int first[MAXN], nxt[MAXN], to[MAXN], num[MAXN], tot, len[MAXN];
int low[MAXN], dfn[MAXN], index, q[MAXN], tail, instack[MAXN], cnt;
int du[MAXN], sze[MAXN], ans[MAXN], huan[MAXN], ans1[MAXN];
int first1[MAXN], nxt1[MAXN], to1[MAXN], tot1, len1[MAXN], dep[MAXN], size[MAXN];
int ans3[MAXN], du1[MAXN];
LL ans2[MAXN], k;
inline void addedge(int x, int y, int w) {
nxt[++tot] = first[x]; first[x] = tot; to[tot] = y; len[tot] = w;
nxt1[++tot1] = first1[y]; first1[y] = tot1; to1[tot1] = x; len1[tot] = w;
}
inline void Tarjan(int u) {
low[u] = dfn[u] = ++index;
q[++tail] = u; instack[u] = true;
for(int i = first[u]; i; i = nxt[i]) {
int v = to[i];
if(!dfn[v])
Tarjan(v), low[u] = min(low[v], low[u]);
else if(instack[v])
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
++cnt;
int now = tail;
while(q[now] != u) ++sze[cnt], --now; ++sze[cnt];
while(q[tail] != u) {
int v = q[tail];
instack[v] = false;
num[v] = cnt;
ans[v] = sze[cnt] - 1;
if(sze[cnt] > 1) huan[v] = 1;
--tail;
}
instack[u] = false; num[u] = cnt;
ans[u] = sze[cnt] - 1; --tail;
if(sze[cnt] > 1) huan[u] = 1;
ans1[cnt] = u;
}
}
inline void dfs(int x) {
for(int i = first[x]; i; i = nxt[i]) {
int v = to[i];
if(ans[v]) { ans[x] = ans[v] + 1; return; }
dfs(v);
ans[x] = ans[v] + 1;
}
}
inline void dfs1(int x, int fa, int y) {
size[x] = 1;low[x] = -1;
for(int i = first1[x]; i; i = nxt1[i]) {
int v = to1[i];
if(huan[v]) continue;
dep[v] = dep[x] + 1;
dfs1(v, x, y);
size[x] += size[v]; // size -the size of tree
ans2[i] = ((LL)size[v] % mod) * ((LL)(dep[x] + sze[y]) % mod) % mod; // sze -the size of circle
}
}
inline void work(int x, int y) {
dfs1(x, 0, num[x]);
ans3[num[x]] += size[x]; //ans3 环包括子树的大小
k += (LL)(size[x] - 1) * y % mod;
for(int i = first1[x]; i; i = nxt1[i]) {
if(!size[to1[i]])
work(to1[i], y - 1);
}
}
inline void solve(int x) {
for(int i = first[x]; i; i = nxt[i]) {
if(ans2[i]) continue;
ans2[i] = (k + (LL)sze[num[x]] * (LL)(sze[num[x]] - 1) / 2) % mod;
k -= ans3[num[x]] - sze[num[x]]; k = (k % mod + mod) % mod;
k += (LL)sze[num[x]] * (size[to[i]] - 1) % mod;
solve(to[i]); // ans2 每条边的贡献
}
}
inline void dfs2(int x, int fa) {
dep[x] = dep[fa] + 1;
size[x] = 1; low[x] = -1;
for(int i = first1[x]; i; i = nxt1[i]) {
int v = to1[i];
if(huan[v]) continue;
dfs2(v, x);
size[x] += size[v];
ans2[i] = (LL)size[v] * dep[x] % mod;
}
}
int main() {
int n = read();
for(int i = 1; i <= n; ++i) {
int x = read(), y = read();
if(i == x) continue;
++du[x]; ++du1[i];
addedge(i, x, y);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i])
Tarjan(i);
for(int i = 1; i <= n; ++i)
if(!du[i]) dfs(i);
LL a = 0;
for(int i = 1; i <= cnt; ++i) {
if(sze[i] == 1) continue;
k = 0;
work(ans1[i], sze[i] - 1);
solve(ans1[i]);
}
for(int i = 1; i <= n; ++i) {
if(low[i] != -1 && !du1[i])
dfs2(i, 0);
}
for(int i = 1; i <= n; ++i) {
for(int u = first[i]; u; u = nxt[u])
a = (a + (LL)ans2[u] * len[u] % mod) % mod;
a = (a - (n - 1 - ans[i]) + mod) % mod;
}
cout<