[20220404联考] 条条下水道通祖安

前言

这个构造又不是构造可行解,是最小化答案,不推式子不打表找规律直接想当然写个做法有分就有鬼了。

有鬼!想当然做法可以过样例,数据有样例!

题目

没有链接

俗话说得好,条条下水道通祖安。这天祖安有打算建一些下水道通道来使得 \(n\) 个地点达到要求:

  • 为方便管理,每个点度数至少要有 \(k\)

  • 为防止藩镇割据,度数为 \(k\) 的点两两间不能有通道。

  • 两点间至多建一条通道。

  • 为了节省经费,建造的通道要最少。

你能帮帮祖安的城市设计师吗?

样例输入

5 2

样例输出

6
1 2
1 3
1 4
2 5
3 5
4 5

\(2\le k\le 10;2k+1\le n\le 1000.\)

讲解

想当然做法是 \(n-k\) 个点另外 \(k\) 个点都连边,除了样例啥也过不了。

我们这样想,我们要使连边最少肯定有点度数为 \(k\),我们试图枚举它的数量,但是为了和代码统一,我们记度数大于 \(k\) 的点有 \(x\) 个(左部),度数恰好为 \(k\) 的点就有 \(n-x\) 个(右部)。

令左部点内部有 \(y\) 条边, 那么有 \(k(n-x)+2y\ge (k+1)x\),我们需要最小化 \(k(n-x)+y\),可以发现枚举 \(x\) 后只需最小化 \(y\)

随便化一化式子发现 \(y=\max\{0,\lceil\frac{(2k+1)x-kn}{2}\rceil\}\),这样得到了最小的 \(k(n-x)+y\) 对应的 \(x\) 之后,思考如何构造一组解。

首先把左右两部的 \(k(n-x)\) 条连边依次均匀连接,不难发现均匀连接是不劣的。

然后把左部点度数依然为 \(k\) 的边两两匹配连边,如果有单出来的随便再连一条即可。

代码

//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 1005;
int n,k;

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int tot,deg[MAXN];
pair<int,int> e[MAXN*MAXN];
bool vis[MAXN][MAXN];
void Add_Edge(int u,int v){
	e[++tot] = make_pair(u,v);
	++deg[u]; ++deg[v];
	vis[u][v] = vis[v][u] = 1;
}
int calc(int x){return k * (n-x) + Max(0,(int)ceil(0.5 * ((2*k+1)*x-k*n)));}

int main()
{
//	freopen("graph.in","r",stdin);
//	freopen("graph.out","w",stdout);
	n = Read(); k = Read(); int lef = 0,MIN = n*n+4;
	for(int i = 1;i <= n;++ i){
		int val = calc(i);
		if(val < MIN) MIN = val,lef = i;
	}
	for(int i = lef+1,lst = 1;i <= n;++ i)
		for(int j = 1;j <= k;++ j,lst = lst % lef + 1)
			Add_Edge(lst,i);
	int lst = 0;
	for(int i = 1;i <= lef;++ i)
		if(deg[i] == k){
			if(lst) Add_Edge(lst,i),lst = 0;
			else lst = i;
		}
	if(lst){
		for(int i = 1;i <= lef;++ i)
			if((lst ^ i) && !vis[i][lst])
				{Add_Edge(i,lst);break;}
	}
	Put(tot,'\n');
	for(int i = 1;i <= tot;++ i) Put(e[i].first,' '),Put(e[i].second,'\n');
	return 0;
}

总结?

最小化式构造先找最小值,再构造。

posted @ 2022-04-04 16:54  皮皮刘  阅读(43)  评论(0编辑  收藏  举报