[NOIP模拟] 路径统计

About :

     2017.11.8 T2

Solution :

    这道题坑了我一天的时间,下次我要记住合理的安排时间,T3 其实非常简单的,在T2上浪费了太多的时间。
    讲一下思路,先找到环,由于图中每个点只有一条出边,而且每个点都作为一条边的起点,所以,环只有一种且环一定是图的尾部。那我们先 Tarjan 找到环,建反向边,用反向边查找环上每个节点向下的子树,对于子树中的边的贡献,也就是它会被经过的次数 :

(Sizecircle+deep[father])Sizeson

    对于本来就不在环上的点(这种情况存在是因为不考虑子环,题目是有子环的),也满足这个性质,因为他们也是一棵树。
    对于环上的边的贡献,我们可以发现成一种递减数列,我们记 Sizetree 是每个环上的节点的子树大小(包括自己), 那么我们可以发现这条边的贡献 :
Sizecircle+Sizetreeu(Sizecircle1)+ ...+ Sizetreei1

    也就是说从当前点沿反向边的方向递减,我们考虑如何 O(1) 维护这种递减性质,我们沿反向边计算,这里令
k=Sizetreeu(Sizecircle1)+ ...+ Sizetreei1
    这个环加子树的大小我们定义为 Sizeall 那么每次转移就是 :
k=kSizeall+SizecirlceSizetreenow

    然后统计答案就好了,其中注意取模问题。


Code :

#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<

你可能感兴趣的:(NOIP模拟,图论,Tarjan)