T13656 NOI接站

题目背景

2017.10.11

leoly的选讲题
题目描述

NOI2016开始了,来自全国各省的选手聚集到了绵阳市的n个车站,编号为1到n,每个车站都有一名选手到来。

绵阳市的道路错综复杂,车站之间的路都是单行路。且图中可能存在重边和自环。

大巴车从南山中学出发,经过若干车站,最后回到南山中学。大巴车每经过一个车站,都会接走那个车站里的选手(若该选手还没有被接走)。每个车站和南山中学均双向连通。同一个车站可以经过多辆大巴车,一辆大巴车也可以多次经过同一个车站。

南山中学的老师希望用尽量少的大巴车,接走所有车站的选手,请你告诉南山中学的老师至少需要多少辆大巴车。
输入输出格式

输入格式:

第一行一个整数t,表示数据组数。

对于每组数据:

第一行两个整数n,m。

接下来m行,每行两个整数x、y,表示编号为x的车站到编号为y的车站有一条单行路。

输出格式:

t行,每行对应该组数据的答案。
输入输出样例

输入样例#1: 复制

1
4 3
1 2
2 3
2 4

输出样例#1: 复制

2

说明

30%的数据,1<=n<=10,0<=m<=20

100%的数据,1<=t<=10,1<=n<=200,0<=m<=1000

时间限制:1s

内存限制:128M

不得不%leoly orz 这题带环 然而我对最少路径覆盖一开始理解不清楚 导致没有tarjan 就做 死的很惨

所以只需要tarjan缩环 然后再floyd传递闭包 最后跑一个最大流 算出答案即可

#include<queue>
#include<cstdio>
#include<stack>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 2200
using namespace std; 
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++; 
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
struct node{
    int y,z,next;
}data[N*N],data1[N*N];
int h[N],level[N],num=1,t,T,mp[N][N],n,m,cnt,b[N],dfn[N],low[N],stackf[N],s,h1[N];
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;
}
inline bool bfs(){
    queue<int>q;q.push(0);memset(level,0,sizeof(level));level[0]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if(level[y]||!z) continue;level[y]=level[x]+1;q.push(y);if (y==T) return 1;
        }
    }return 0;
}
inline int dfs(int x,int s){
    if (x==T) return s;int ss=s;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (level[x]+1==level[y]&&z){
            int xx=dfs(y,min(z,s));if(!xx) level[y]=0;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if (!s) return ss;
        }
    }return ss-s;
}stack<int> qq;
inline void tarjan(int x){
    dfn[x]=++cnt;low[x]=cnt;stackf[x]=1;qq.push(x);
    for (int i=h1[x];i;i=data1[i].next){
        int y=data1[i].y;
        if (!dfn[y]) tarjan(y),low[x]=min(low[y],low[x]);else if(stackf[y]) low[x]=min(low[x],dfn[y]);
    }
    if (low[x]==dfn[x]){
        s++;int y;
        do{
            y=qq.top();stackf[y]=0;qq.pop();b[y]=s;
        }while(y!=x);
    }
}
int main(){
    freopen("cia.in","r",stdin);
    t=read();
    while(t--){
        n=read();m=read();memset(mp,0,sizeof(mp));T=n*2+1;num=0;memset(h1,0,sizeof(h1));memset(dfn,0,sizeof(dfn));cnt=0;s=0;
        for (int i=1;i<=m;++i){
            int x=read(),y=read();data1[++num].y=y;data1[num].next=h1[x];h1[x]=num;
        }
        for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i);memset(h,0,sizeof(h));num=1;
        //for (int i=1;i<=n;++i) printf("%d ",b[i]);printf("\n");
        for (int i=1;i<=n;++i){
            for (int j=h1[i];j;j=data1[j].next){
                int y=data1[j].y;
                if(b[i]!=b[y]) mp[b[i]][b[y]]=1;
            }
        }//printf("%d\n",s);
        for (int k=1;k<=s;++k)
            for (int i=1;i<=s;++i)
                for (int j=1;j<=s;++j)
                    mp[i][j]|=mp[i][k]&mp[k][j];
        //for (int i=1;i<=n;++i) printf("%d %d\n",i,mp[3][i]);
        for (int i=1;i<=s;++i){
            insert1(0,i,1);
            for (int j=1;j<=s;++j) if (i!=j&&mp[i][j]) insert1(i,j+s,1);
            insert1(i+s,T,1);
        }int ans=0;
        while(bfs()) ans+=dfs(0,inf);
        printf("%d\n",s-ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值