1、
706. [最小割模板]巡逻
题目描述
FJ有个农场,其中有n块土地,由m条边连起来。FJ的养牛场在土地1,在土地n有个新开张的雪糕店。Bessie经常偷偷溜到雪糕店,当Bessie去的时候,FJ就要跟上她。但是Bessie很聪明,她在从雪糕店返回时不会经过去雪糕店时经过的农场,因此FJ总是抓不住Bessie。 为了防止Bessie生病,FJ决定把一些诚实的狗放在一些土地(1和n除外)上,使Bessie无法在满足每块土地最多只经过一次的条件的情况下,从养牛场溜到雪糕店然后又溜回养牛场。 求出FJ最少要放多少只狗。数据保证1和n间没有直接的连边。
输入格式
Line 1: Two integers: N and M.
Lines 2..M+1: Each line contains two integers A and B that describea trail joining fields A and B. It is possible to travelalong this trail both ways. No trail will appear twice.
输出格式
Line 1: A single integer that tells how many fields FJ should guard.
input
6 7
1 2
2 6
1 3
3 4
3 5
4 6
5 6
output
1
OUTPUT DETAILS:
FJ can guard field 2 (for example). FJ could guard both fields 4 and
5, but that would require more dogs.
数据规模与约定
数据范围:n<=1000,m<=10000。
时间限制:1s
空间限制:256MB
一道最小割的模板题。
显而易见的,我们可以想到把第i个点拆成i和i+n,把两个点连起来,权值为1。那么最小割问题就转化为最大流了。
这道题的问题就是两个点之间的边到底怎么连。
i为入点,i+n为出点。
那么所有由出点到入点的边权值为0。
由入点到出点的边权值为1(任意正整数都行)。
所以对于两个点a,b
我们只要insert(a+n,b,1),(b+n,a,1) 那么只要套一下dinic模板就行了。
#include<bits/stdc++.h>
using namespace std;
const int oo=165430000;
bool check;
int n,m,lazy,ans,linkk[21000],t,deep[21000],head,tail,q[21000];
struct node
{
int n,y,v;
}e[1100000];
int read()
{
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void insert(int aa,int bb,int cc)
{
e[++t].n=linkk[aa];
e[t].y=bb;
e[t].v=cc;
linkk[aa]=t;
e[++t].n=linkk[bb];
e[t].y=aa;
e[t].v=0;
linkk[bb]=t;
return;
}
void init()
{
n=read();m=read();
int aa,bb;
t=1;
for(int i=1;i<=m;++i) //点①
{
aa=read();bb=read();
if(aa>bb) swap(aa,bb);
if(aa!=1)
insert(aa+n,bb,oo),insert(bb+n,aa,oo);
else
insert(1,bb,oo),insert(bb,1,oo);
}
for(int i=2;i<=n-1;++i)
insert(i,i+n,1);
return;
}
int dfs(int x,int lazy)
{
if(x==n)return lazy;
int nowlazy=0;
int d=0;
for(int i=linkk[x];i&&nowlazy<lazy;i=e[i].n)
{
int v=e[i].y;
if(deep[v]==deep[x]+1&&e[i].v>0)
if(d=dfs(v,min(e[i].v,lazy-nowlazy)))
{
nowlazy+=d;
e[i].v-=d;
e[i^1].v+=d;
}
}
if(nowlazy==0) deep[x]=-1;
return nowlazy;
}
void bfs()
{
memset(deep,-1,sizeof(deep));
deep[1]=0;head=tail=1;
q[1]=1;
while(head<=tail)
{
int v=q[head];
for(int i=linkk[v];i;i=e[i].n)
if(deep[e[i].y]==-1&&e[i].v>0) deep[e[i].y]=deep[v]+1,q[++tail]=e[i].y;
head++;
}
if(deep[n]!=-1) check=true;
return;
}
int main()
{
init();
check=true;
while(check)
{
int d=0;check=false;
bfs();
if(!check) break;
while(d=dfs(1,oo)) ans+=d;
}
if(ans!=0)
printf("%d",ans-1);
else printf("0");
return 0;
}
tarjan题写的太少了 模拟赛gg了,于是复习一下tarjan。
2、
336. 【dfn】交换机
题目描述
n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。
但备用 交换机数量有限,不能全部配备,只能给部分重要城市配置。
于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备 用交换机。
请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。
友情提示:图论常见的坑点,重边,自环,还有对本题来说的不连通
输入格式
第一行,一个整数n,表示共有n个城市(2<=n<=20000)
下面有若干行(<=60000):每行2个数a、b,a、b是城市编号,表示a与b之间有直接通讯线路。
输出格式
第一行,1个整数m,表示需m个备用交换机,下面有m行,每行有一个整数,表示需配备交换机的城市编号,输出顺序按编号由小到大。如果没有城市需配备备用交换机则输出0。
样例数据
input
7
1 2
2 3
2 4
3 4
4 5
4 6
4 7
5 6
6 7
output
2
2
4
数据规模与约定
gdoi
时间限制:1s1s
空间限制:256MB
#include<bits/stdc++.h>
using namespace std;
int n,m,linkk[21000],t,root,dfn[21000],low[21000],ind,tot,ans[21000];
bool flag[21000];
struct node{
int n,y;
}e[121000];
int read() {
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void insert(int aa,int bb){
e[++t].n=linkk[aa];
e[t].y=bb;
linkk[aa]=t;
e[++t].n=linkk[bb];
e[t].y=aa;
linkk[bb]=t;
return;
}
void tarjan(int x){
dfn[x]=low[x]=++ind;
int son=0;
for(int i=linkk[x];i;i=e[i].n){
int v=e[i].y;
if(!dfn[v]){
tarjan(v);
son++;
if(low[v]<low[x]) low[x]=low[v];
if(!flag[x])
if( ((x == root) && (son > 1)) || ( ( x != root ) && (low[v] >= dfn[x]) ) )
tot++,flag[x]=true;
}
else
if(dfn[v]<low[x])
low[x]=dfn[v];
}
return;
}
void init(){
n=read();
int aa,bb;
while(scanf("%d",&aa)!=EOF){
bb=read();
insert(aa,bb);
}
for(int i = 1;i <= n;++i)
if(!dfn[i]){
root=i;
tarjan(i);
}
return;
}
int main() {
init();
sort(ans+1 , ans+1+tot);
printf("%d\n",tot);
for(int i = 1;i <= n;++i)
if(flag[i]) printf("%d\n",i);
return 0;
}