Description:
一个图有n+m个顶点,顶点分黑白两种颜色,其中编号1~n的为白色顶点,n+1~n+m的为黑色顶点。
对于任意一个白色顶点vi,有且仅有a个不同的白色顶点和b个不同的黑色顶点与之相连。
对于任意一个黑色顶点ui,有且仅有c个不同的白色顶点和d个不同的黑色顶点与之相连。
你的任务是根据给出的a,b,c,d,将原图构造出来,如果有多种构造方案,则输出n+m最小的方案,如果还有多种方案,则输出任意一种。
a,b,c,d<=50
题解:
最显然的是n*b=m*c
点数等于边数除以2,所以n*a和m*d都必须是二的倍数。
n>c,m>b
用上面的条件约束就可以得到n,m
接下来的构造考虑两个部分。
1.1黑1白
这个显然按顺序第一个白连1-a,第二个连a+1-2a,以此类推,如果超过了m就回到1。
2.二白或二黑
和之前的类似,只是超过了m回到i+1,因为比它小的已经连过了,记录有多少个比它小的和它连了以算出还需要连多少个比它大的。
Code:
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int a, b, c, d, n, m, bb, cc, z[N];
int gcd(int a, int b) {return !b ? a : gcd(b, a % b);}
void add(int x, int y) {printf("%d %d\n", x, y);}
int ss[N];
void GG(int n, int a, int p) {
memset(ss, 0, sizeof ss);
int x = 2;
fo(i, 1, n) {
fo(j, 1, a - ss[i]) {
if(x <= i) x = i + 1;
ss[x] ++; add(i + p, x + p);
x = x % n + 1;
}
}
}
int main() {
scanf("%d %d %d %d", &a, &b, &c, &d);
bb = b / gcd(b, c); cc = c / gcd(b, c);
n = c; m = b;
while(n < a + 1 || m < d + 1 || (n * a % 2) || (m * d % 2)) n += cc, m += bb;
printf("%d %d\n", n, m);
fo(i, 1, n) fo(j, 1, b) z[++ z[0]] = i;
z[0] = 0;
fo(i, 1, c) fo(j, 1, m) add(j + n, z[++ z[0]]);
GG(n, a, 0); GG(m, d, n);
}