• HDU5739-Fantasia(tarjan求割点)


    题意:给一个无向图n个点1~n,m条边,sigma(i*zi)%(1e9+7)。zi是这个图删掉i点之后的价值。一个图的价值是所有连通子图的价值之和,连通图的价值是每个点的乘积。

    题解:讲道理这题不算难。注意一点就是一开始给的图不一定是连通的。然后就是割点会把一个连通图分成两个连通图,而其他点不影响图的连通性。至于割点直接tarjan就可以了。

       而且dfs的过程中也可以处理出答案。这题只需要把每个连通子图求出而不需要处理出强连通分量,所以省去了tarjan算法中的stack数组。

       变量比较多,写起来很烦。。

    AC代码(1934MS):

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <vector>
    #include <math.h>
    using namespace std;
    typedef long long ll;
    const int N = 200010;
    const ll mod = 1e9+7;
    int n, m;                   // 同题目描述
    int a[N];                   // 记录每个点的权值
    
    struct Edge                 // 前向星存边
    {
        int to, next;
    } edge[N*4];
    int cnt_edge;
    int head[N];
    void add_edge(int u, int v)
    {
        edge[cnt_edge].to = v;
        edge[cnt_edge].next = head[u];
        head[u] = cnt_edge++;
    }
    
    int dfn[N], low[N], idx;        // tarjan中用到的变量
    
    ll mul;                         // 临时变量,记录每个图的所有点乘积
    ll ans[N];                      // 对于每个割点 如果分割了这个点 这棵树的答案
    ll smul[N];                     // 对于每个割点 分割它后所有分出来的子树的乘积
    bool cut[N];                    // 割点
    int graph_cnt;                  // 连通图数量
    vector<int> G[N];               // 记录每个连通图
    ll graph[N];                    // 每个连通图所有点的乘积
    int kind[N];                    // 每个点属于哪个连通图
    
    
    void init(int n)
    {
        for (int i = 0; i <= n; ++i) {
            head[i] = -1;
            dfn[i] = 0;
            ans[i] = 0;
            smul[i] = 1;
            cut[i] = false;
            G[i].clear();
        }
        cnt_edge = 0;
        idx = 0;
        graph_cnt = 0;
    }
    
    ll pow_mod(ll x, ll n)
    {
        ll ans = 1;
        while (n) {
            if (n & 1) ans = ans * x % mod;
            x = x * x % mod;
            n >>= 1;
        }
        return ans;
    }
    
    void tarjan(int u, int fa)
    {
        dfn[u] = low[u] = ++idx;
        int child = 0;
        G[graph_cnt].push_back(u);
        kind[u] = graph_cnt;
        mul = mul * a[u] % mod;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (v == fa) continue;
            if (!dfn[v]) {
                ll tmp = mul;
                tarjan(v, u);
                low[u] = min(low[u], low[v]);
                if (low[v] >= dfn[u]) {     // u是割点
                    ++child;
                    ll inv = pow_mod(tmp, mod-2);
                    tmp = mul * inv % mod;  // mul/tmp 就是这个子图的乘积
                    ans[u] = (ans[u] + tmp) % mod;
                    smul[u] = smul[u] * tmp % mod;
                }
            } else {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if ((fa == -1 && child > 1) || (fa != -1 && child)) cut[u] = true; // u是割点
    }
    
    
    int main(int argc, char const *argv[])
    {
        //freopen("in", "r", stdin);
        int T;
        cin >> T;
        while (T--) {
            scanf("%d%d", &n, &m);init(n);
            for (int i = 1; i <= n; ++i) scanf("%d", a+i);
            int u, v;
            while (m--) {
                scanf("%d%d", &u, &v);
                add_edge(u, v);
                add_edge(v, u);
            }
            ll res = 0;
            for (int i = 1; i <= n; ++i) {
                if (!dfn[i]) {
                    mul = 1;
                    tarjan(i, -1);
                    graph[graph_cnt] = mul;
                    res = (res + mul) % mod;
                    for (unsigned j = 0; j < G[graph_cnt].size(); ++j) {
                        u = G[graph_cnt][j];
                        if (u != i) {
                            ans[u] = (ans[u] + mul * pow_mod(smul[u]*a[u]%mod, mod - 2) % mod) % mod;  //mul/smul[j];
                        }
                    }
                    ++graph_cnt;
                }
            }
            ll rut = 0;
            for (int i = 1; i <= n; ++i) {
                ll tmp;
                if (cut[i]) {               // 如果是割点的话 就是这个点所在图分成多个子图
                    tmp = (res - graph[ kind[i] ] + ans[i] + mod) % mod;
                } else {
                    if (G[kind[i]].size() == 1) {
                        tmp = (res - a[i] + mod) % mod;
                    } else {
                        tmp = (res - graph[kind[i]] + graph[kind[i]] * pow_mod(a[i], mod-2) % mod + mod) % mod;
                    }
                }
                rut = (rut + i * tmp % mod) % mod;
            }
            cout << rut << endl;
         }
        return 0;
    }
  • 相关阅读:
    in_array()和explode()的使用笔记
    写sql语句连接的时候注意的一个小细节
    在thinkphp框架模板中引用session
    查询数据库所有(某个)表中字段名,数据类型,说明等,导出数据字典
    (委托事件处理)关于多线程执行显示进度条的实例(转)&&线程间操作无效: 从不是创建控件“rtxtEntryNO”的线程访问它。
    判断当前线程所处的状态 (转)以及终止当前线程
    string 字符串的分隔处理与list的相互转换
    C# 动态调用webservice
    C#中的List<string>泛型类示例
    命名空间"system.web"中不存在类型或命名空间名称security"
  • 原文地址:https://www.cnblogs.com/wenruo/p/5712361.html
Copyright © 2020-2023  润新知