jzoj 5589. 缩点

Description
给定一张n个点m条边的无向图。每次你可以选择两个点u,v,将它们合并为一个点w:对于原先的一条边(u,i)或(v,i),连边(w,i);对于原先的一条边(u,u),(v,v)或(u,v),连边(w,w)。每次缩点后图的点数减小1,边数不变。
求最少需要几次缩点才能使图满足以下条件:(1)不存在重边和简单环,但可以存在自环;(2)存在一条路径使得所有不在路径上的点到它的距离为1。
 
Input
第一行两个整数n,m,接下来m行每行三个整数ui,vi表示一条边。保证不存在重边。
Subtask 1 (10pts):n,m<=10。
Subtask 2 (36pts):n,m<=2000。
Subtask 3 (31pts):给定的图是一棵树。
Subtask 4 (23pts):无特殊限制。
对于全部数据,1<=n,m<=10^5。
Output
输出一行一个整数表示答案。
 
Sample Input
输入1:
4 4
1 2
2 3
3 4
4 2
输入2:
6 3
1 2
3 4
5 6
输入3:
7 6
1 2
2 3
1 4
4 5
1 6
6 7
Sample Output
输出1:
2
输出2:
2
输出3:
1
 
Code

  

#include<bits/stdc++.h>
#define eho(X,x) for(int i=X.head[x];i;i=X.net[i])
#define v G1.fall[i]
#define V G2.fall[i]
#define N 100007
#define pii pair<int,int>
using namespace std;
int n,col[N],nwa[N],nwb[N],cnt,low[N],dfn[N],cn1,usd[N<<1],op;
map<pii,int> ma;
struct Graph{
    int tot,head[N],net[N<<1],fall[N<<1];
    Graph() {tot=1;}
    void add(int x,int y){
        fall[++tot]=y; net[tot]=head[x]; head[x]=tot;
    }
    void adds(int x,int y){add(x,y); add(y,x);}
}G1,G2;
int s[N],tp;
int m,x,y,color,ans,us2[N],lef,alg,dis[N],top,he,siz,XX,YY;
void tarjan(int x,int f) {
    dfn[x]=low[x]=++cnt,s[tp++]=x;
    eho(G1,x) if (v!=f) {if (!dfn[v]) tarjan(v,x);low[x]=min(low[x],low[v]);}
    if (low[x]==dfn[x]) { color++;do {col[s[--tp]]=color;}while (s[tp]!=x);}
}
//void tarjan(int x,int f_eg) {
//    dfn[x]=low[x]=++cnt;
//    eho(G1,x) {
//        if (!dfn[v]) {
//            tarjan(v,i);
//            low[x]=min(low[x],low[v]);
//            if (low[v]>low[x]) {
//             usd[i]=usd[i^1]=1; nwa[++cn1]=G1.fall[i]; nwb[cn1]=G1.fall[i^1];
//            }
//        } else if (i^f_eg^1) low[x]=min(low[x],dfn[v]);
//    }
//}
//void dfs(int x,int color) {
//    col[x]=color;
//    eho(G1,x) if (!usd[i]&&!col[v]) dfs(v,color);
//}

void Dfs2(int x,int fa){
    int alg=!!fa;
    us2[x]=1;
    eho(G2,x) if (V^fa) {
        alg++; Dfs2(V,x);
    }
    lef+=alg<=1;
}
void Dfs(int x,int fa){
//    cerr<<x<<endl;
    dis[x]=dis[fa]+1;
    eho(G2,x) if (V^fa) Dfs(V,x); 
    if (dis[x]>dis[top]) top=x;
}
void work(int x) {
    lef=0; siz++;
    Dfs2(x,0);
    top=0,Dfs(x,0); he=top;
    top=0,Dfs(he,0); 
    ans+=dis[top]>1?lef+dis[top]-2:1;
    op+=lef;
}
signed main () {
    freopen("merge.in","r",stdin);
    freopen("merge.out","w",stdout);

    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) {
        scanf("%d%d",&x,&y);
        G1.adds(x,y);
    }
    for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i,0);
//    for (int i=1;i<=n;i++) {
//        if (col[i]) continue;
//        dfs(i,++color);
//    } 
    int pi=0;
//    assert(p)
//    for (int i=1;i<=cn1;i++)  {
//       XX=col[nwa[i]]; YY=col[nwb[i]];
//       if (XX>YY) swap(XX,YY);
//       if (ma.count(pii(XX,YY))) continue;
//       G2.adds(XX,YY);
//       ma[pii(XX,YY)]=1; pi++;
//    }
//    assert(cn1==pi);
    for (int j=1;j<=n;j++) eho(G1,j) if (col[j]!=col[v]) G2.add(col[j],col[v]),pi++;
//    cerr<<pi<<endl;
    for (int i=1;i<=color;i++)  if (!us2[i]) work(i);
    printf("%d\n",n-ans+siz-1);
//    cerr<<pi/2+siz<<endl;
    cerr<<op<<endl;
//    assert(pi+siz-1==color);
    return 0;
}

 

 

 

转载于:https://www.cnblogs.com/rrsb/p/9878888.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值