Mining Your Own Business

来源:互联网 发布:网络玄幻小说合集目录 编辑:程序博客网 时间:2024/05/16 17:52

题目链接

  • 题意:
    给定一个无向连通图,选择一些特殊点,当图中任意一个点(也有可能是特殊点)被删去的时候,其他点至少能到达一个特殊点
    输入:n表示边数;每行两个数表示一条边(从1开始)
    输出:需要的最少的特殊点和总的方案数
  • 分析:
    首先,如果没有删去点,所有的点都可以到达任意一个特殊点(连通)。主要是分析连通性,所以从割点入手(不要忘了可以删特殊点)
    显然,特殊点如果选割点的话是不划算的。
    对于每一个割点个数为1的图,如果图中没有特殊点,那么至少删去割点,这个图就不能到达特殊点,所以这样的图必须有一个特殊点
    之后,对于任意一个图,如果删除图中的点,那么不影响(不影响连通性);如果删除这个图的一个割点,那么总有一个割点为1的图与之连通

    (个人理解)将所有的点双连通分量缩成一个点,割点变成连接两个点双连通分量的边,那么生成的图是无向无环的图(树)
    那么,删去的点认为就是一个双连通分量(即生成的图中的一个点),删去的边认为是删去了一个割点
    问题就变成了在树中删去一个点或者边,使得所有的点都能到达特殊点,也就是所有的根和叶子为特殊点
  • 特殊情况:当只有一个连通分量时候需要单独考虑,即两个特殊点
  • 重点:
    选取特殊点的位置不能是割点
    特殊点必选在割点数量为1的图中
    考虑到没有割点的图的情况
//无向图的双连通分量//每次调用前手动清空vector<int> G//使用时只更新G完成构图//bcc_cnt从1开始计数//pre[]表示点在DFS树中的先序时间戳//iscut[]表示点是否为割点//bccno[]表示点所在的双连通分量编号//bcc_cnt表示双连通分量个数//vector<int> G保存每个点相邻的下一个点序号//vector<int> bcc保存每个双连通分量的点集,算法运行过程动态清空//stack<Edge> S是算法用到的栈const int MAXV = 51000;const int MAXE = 51000;int pre[MAXV], iscut[MAXV], bccno[MAXV], dfs_clock, bcc_cnt; // 割顶的bccno无意义struct Edge{    int u, v;};vector<int> G[MAXV], bcc[MAXV];stack<Edge> S;int dfs(int u, int fa){    int lowu = pre[u] = ++dfs_clock;    int child = 0;    for(int i = 0; i < G[u].size(); i++)    {        int v = G[u][i];        Edge e = (Edge){u, v};        if(!pre[v])   // 没有访问过v        {            S.push(e);            child++;            int lowv = dfs(v, u);            lowu = min(lowu, lowv); // 用后代的low函数更新自己            if(lowv >= pre[u])            {                iscut[u] = true;                bcc_cnt++;                bcc[bcc_cnt].clear();                for(;;)                {                    Edge x = S.top();                    S.pop();                    if(bccno[x.u] != bcc_cnt)                    {                        bcc[bcc_cnt].push_back(x.u);                        bccno[x.u] = bcc_cnt;                    }                    if(bccno[x.v] != bcc_cnt)                    {                        bcc[bcc_cnt].push_back(x.v);                        bccno[x.v] = bcc_cnt;                    }                    if(x.u == u && x.v == v) break;                }            }        }        else if(pre[v] < pre[u] && v != fa)        {            S.push(e);            lowu = min(lowu, pre[v]); // 用反向边更新自己        }    }    if(fa < 0 && child == 1) iscut[u] = 0;    return lowu;}void find_bcc(int n){    // 调用结束后S保证为空,所以不用清空    memset(pre, 0, sizeof(pre));    memset(iscut, 0, sizeof(iscut));    memset(bccno, 0, sizeof(bccno));    dfs_clock = bcc_cnt = 0;    for(int i = 0; i < n; i++)        if(!pre[i]) dfs(i, -1);};int main(){//    freopen("in.txt", "r", stdin);    int n, a, b, kase = 1;    while (~RI(n) && n)    {        REP(i, MAXV) G[i].clear();        int Max = 0;        REP(i, n)        {            RII(a, b); a--; b--;            Max = max(Max, max(a, b));            G[a].push_back(b);            G[b].push_back(a);        }        find_bcc(Max);        LL ans = 1, cnt = 0;        if (bcc_cnt == 1)        {            LL m = bcc[1].size();            ans = m * (m - 1) / 2;            cnt = 2;        }        else        {            FE(i, 1, bcc_cnt)            {                int tcnt = 0;                REP(j, bcc[i].size())                {                    if (iscut[bcc[i][j]])                        tcnt++;                }                if (tcnt == 1)                {                    ans *= (bcc[i].size() - 1);                    cnt++;                }            }        }        printf("Case %d: ", kase++);        cout << cnt << ' ' << ans << endl;    }    return 0;}


12 0
原创粉丝点击