[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;
}
总结?
最小化式构造先找最小值,再构造。
标签:度数,通祖安,le,return,int,TT,MAXN,20220404,联考 来源: https://www.cnblogs.com/PPLPPL/p/16099703.html