原始生物
https://loj.ac/problem/10112
题目描述
给出n个约束条件\((l,r)\),在S这个序列中存在连续的两个数是\(l\)、\(r\),求满足这些条件的序列的最短长度。
思路
我们考虑以\((l,r)\)作为一条单向边,表示\(l\)后需要加\(r\),因此对于整个图来说,如果它是连通的,那么就是欧拉回路的长度。但图如果不连通,我么就要考虑加最少的边使得图连通并且存在欧拉回路。有向图存在欧拉回路的条件是每个节点的入度等于出度,我们考虑对于单个连通块内,如果要使得这个连通分量内存在欧拉回路,至少要连从\(u\)连出\(in[u]-out[u]\)条边(统计一个的差即可,因为总入度等于总出度),所以累加起来即为满足这个连通分量需要加的边数,我们计为\(a[i]\)。
那么对于每个非孤立点的连通分量,统计答案时,如果\(a[i]≠0\),那么就需要额外连\(a[i]\)条边,这个数目可以包括连通块合并;而\(a[i]=0\),就需要在连出一条边使得连通块之间连通(相应的,为了维护其仍是欧拉图,目标的连通分量也会连出一条边,但这不影响答案的统计,因为目标的分量相当于少连一条入边,多练一条出边)。最后再加上必须的\(n\)条边就是答案。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1100;
int fa[N];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void f_union(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)fa[fx]=fy;
}
int read()
{
int res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
return res*w;
}
int in[N],out[N],a[N];
bool used[N];
int main()
{
int n,m=0;
n=read();
for(int i=1;i<=1000;i++)
fa[i]=i;
for(int i=1;i<=n;i++)
{
int x=read(),y=read();
out[x]++;in[y]++;
m=max(m,max(x,y));
f_union(x,y);
}
for(int i=1;i<=m;i++)
{
int x=find(i);
if(i==find(i)&&in[i]+out[i]>0)used[i]=1;//非孤立点记录且是连通分量的父节点
if(in[i]>out[i])
a[x]+=in[i]-out[i];
}
int ans=0;
for(int i=1;i<=m;i++)
if(used[i]) //这个点代表着一个连通分量
{
if(a[i]!=0)
ans+=a[i];
else ++ans;
}
printf("%d",ans+n);
return 0;
}