Description
一个图有n+m个顶点,顶点分黑白两种颜色,其中编号1~n的为白色顶点,n+1~n+m的为黑色顶点。
对于任意一个白色顶点vi,有且仅有a个不同的白色顶点和b个不同的黑色顶点与之相连。
对于任意一个黑色顶点ui,有且仅有c个不同的白色顶点和d个不同的黑色顶点与之相连。
你的任务是根据给出的a,b,c,d,将原图构造出来,如果有多种构造方案,则输出n+m最小的方案,如果还有多种方案,则输出任意一种。
Input
一行,4个整数a,b,c,d(1<=a,b,c,d<=50),意义如上所述。
Output
第一行两个整数n, m表示有n个白色顶点和m个黑色顶点。
接下来若干行,每行两个整数xi,yi表示标号为xi的顶点和标号为yi的顶点有一条边。编号1~n的为白色顶点,n+1~n+m的为黑色顶点。
你要保证不能出现重复的边(注意,图是双向的)。
Sample Input
1 2 1 2
Sample Output
2 4
1 2
1 3
1 5
2 4
2 6
3 4
3 5
4 6
5 6
Data Constraint
100%的数据,a,b,c,d<=50
赛时
不会,放弃。
正解
一枚构造题:
首先要求出最小的n+m。
因为白点连向b个不同的黑点,黑点连向c个的不同的白点。
所以可得nb=mc,设gcd(b,c)=gc,那么nb/gc=mc/gc,因为gcd(b/gc,c/gc)=1,所以可得n=c/gcS,m=b/gcS(S为正整数)
只要再让S取对应的值使得n、m满足
n>=max(a+1,c),m>=max(d+1,b),bn,cm为偶数即可求得最小n+m。
接下来就是连边。对于黑白连边,只要将黑点拼成一环,然后白点在这个环上按照顺
序依次连b个黑点即可。
同理白点与白点和黑点与黑点类似,对于一个白点(黑点)就是 向后按照顺序依次连所需个数个白点(黑点)即可。
按照顺序依次取的意思是如果上个点连到x,
那么这个点就是从 x的下一个点开始连。注意:点不能与自己连边。
#include<cstdio>
#include<iostream>
using namespace std;
int a,b,c,d,gc,n,m,k;
int f[10000];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int main(){
scanf("%d%d%d%d",&a,&b,&c,&d);
gc=gcd(b,c);
for(int i=1;i<=100000;i++){
n=c/gc*i;m=b/gc*i;
if(n>=max(a+1,c)&&m>=max(d+1,b)&&a*n%2==0&&d*m%2==0) break;
}
printf("%d %d\n",n,m);
for(int i=1;i<=n;i++)
for(int j=1;j<=a-f[i];j++){
k=k%n+1;
if(k<=i) k=i+1;
f[k]++;
printf("%d %d\n",i,k);
}
k=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=d-f[n+i];j++){
k=k%m+1;
if(k<=i)k=i+1;
f[n+k]++;
printf("%d %d\n",n+i,n+k);
}
k=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=b;j++){
k=k%m+1;
printf("%d %d\n",i,n+k);
}
}