2015-2016 ACM-ICPC Northeastern European Regional Contest (NEERC 15) C - Cactus Jubilee

C - Cactus Jubilee

思路:

1.如果删掉的是桥,那么将图分成两个部分,设它们的大小分别为a和b,我们为了保证图的连通性,只能连两个部分之间的边,即a*b-1种可能

2.如果删掉的不是桥,那么就是简单环上的边,删掉后环变成了小树,而且我们只能连小树上的点之间的边,否则就会出现一条边在两个简单环中状况,

就不是仙人掌图了(画画图就知道了)。一颗大小为x的小树的贡献是x*(x-1)/2-(x-1),我们可以先把所有小树的贡献和算出来,然后对于每个简单环单独处理,

对于某个简单环我们减去以环上每个点为根的小树的贡献,然后加上断环后形成的新树的贡献就可以知道对于这个环上每条边删掉后的贡献了。

具体实现就是用点双联通求简单环,把桥用并查集连接起来形成小树。

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb emplace_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head

const int N = 1e5 + 5;
vector<int> g[N];
vector<pair<int,int>> bcc[N];
int low[N], dfn[N], sz[N], cc[N], tot = 0, top = 0, cnt = 0, n, m;
pair<int,int> stk[N];
int fa[N];
int Find(int x) {
    if(x == fa[x]) return x;
    else return fa[x] = Find(fa[x]);
}
void Merge(int x, int y) {
    x = Find(x);
    y = Find(y);
    if(x == y) return ;
    fa[x] = y;
    cc[y] += cc[x];
}
LL ans, sum;
void tarjan(int u, int fa) {
    low[u] = dfn[u] = ++tot;
    sz[u] = 1;
    for (int v : g[u]) {
        pair<int,int> e = {u, v};
        if(!dfn[v]) {
            stk[++top] = e;
            tarjan(v, u);
            sz[u] += sz[v];
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) ans += sz[v]*1LL*(n-sz[v])-1;
            if(low[v] >= dfn[u]) {
                cnt++;
                while(stk[top] != e) {
                    bcc[cnt].push_back(stk[top--]);
                }
                bcc[cnt].push_back(stk[top--]);
            }
        }
        else if(v != fa ) {
            if(dfn[v] < dfn[u]) stk[++top] = e, low[u] = min(low[u], dfn[v]);
        }
    }
}
int u, v, k;
int main() {
    freopen("cactus.in", "r", stdin);
    freopen("cactus.out", "w", stdout);
    scanf("%d %d", &n, &m);
    while(m--) {
        scanf("%d %d", &k, &v);
        k--;
        while(k--) {
            scanf("%d", &u);
            g[u].pb(v);
            g[v].pb(u);
            v = u;
        }
    }
    tarjan(1, 1);
    for (int i = 1; i <= n; ++i) fa[i] = i, cc[i] = 1;
    for (int i = 1; i <= cnt; ++i) if(bcc[i].size() == 1) Merge(bcc[i][0].fi, bcc[i][0].se);
    for (int i = 1; i <= n; ++i) if(fa[i] == i) sum += cc[i]*1LL*(cc[i]-1)/2-(cc[i]-1);
    for (int i = 1; i <= cnt; ++i) {
        if(bcc[i].size() == 1) continue;
        LL tmp = sum, tot = 0;
        for (int j = 0; j < bcc[i].size(); ++j) {
            int u = bcc[i][j].fi;
            u = Find(u);
            tmp -= cc[u]*1LL*(cc[u]-1)/2-(cc[u]-1);
            tot += cc[u];
        }

        ans += (tmp+tot*1LL*(tot-1)/2-(tot-1)-1)*1LL*bcc[i].size();

    }
    printf("%lld\n", ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/widsom/p/11153330.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值