【CF】1948E-Clique Partition 题解

传送门:1948E
标签:构造

题目大意

给出两个整数,n 和 k。存在一个有 n 个顶点的图,顶点编号从 1 到 n,初始状态下没有边。你必须给每个顶点分配一个整数;令 ai 为顶点 i 上的整数。所有的 ai 应该是从 1 到 n 的互不相同的整数。分配完整数后,对于每一对顶点 (i, j),如果 |i−j|+|ai−aj|≤k 成立,就在它们之间添加一条边。要求你创建一个图,它可以被分割成尽可能少的完全图(cliques)。图中的每个顶点应该恰好属于一个完全图。

输入:第一行包含一个整数 t (1≤t≤1600) — 测试用例的数量。每个测试用例由一行组成,包含两个整数 n 和 k (2≤n≤40; 1≤k≤2n)。

输出
对于每个测试用例,输出三行:
第一行包含 n 个互不相同的整数 a1, a2, …, an (1≤ai≤n);
第二行包含一个整数 q (1≤q≤n) ;
第三行包含 n 个整数 c1, c2, …, cn (1≤ci≤q)

算法分析

  • 我们需要两步来解决本题:一是分析最大团的大小;二是展示一种构造方法,总是允许我们获得最大可能尺寸的团。
  • 首先我们分析最大团的大小。显然,最大团的大小不能超过k。如果同一个团中有至少k+1个顶点,则至少其中两个顶点(i 和 j)之间的绝对差值大于等于k。由于a_i不等于a_j,那么|a_i - a_j|大于等于1。所以 |i-j| + |a_i - a_j| 至少为 k+1,所以这两个顶点之间不会有边相连(并且不能属于同一个团)。
  • 然后我们要找到一种构造方法,始终允许我们创建k大小的团。尝试解决k=n的问题;如果n > k,我们可以将所有顶点划分为⌈ n/k⌉个团如下:对每个团分配连续的顶点块和要分配给它们的数字(例如,顶点1到k和数字1到k属于第一个团,顶点k+1到2k和数字k+1到2kn属于第二个团),然后在这些块上使用n=k的解决方案。
  • 为了获得n=k的解决方案,我们可以尝试本地暴力破解,比如n≤10,并分析结果。那么其中的构造方法如下:令m = ⌈ n/2⌉; 将所有顶点和数字1到k分成两部分:[1,m]和[m+1,k]; 然后,在每个部分中,顶点索引越大,它得到的整数越小。所以看起来就是:a_1 = m, a_2 = m - 1, …, a_m = 1, a_{m+1} = n, a_{m+2} = n - 1, …, a_n = m + 1. 我们可以证明,任何两个不同半部分的顶点之间的“距离”正好为k,同一半部分内的任何两个顶点之间的距离不超过2(m-1),它永远不会超过k。

代码实现

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,k,ans[80];
signed main(){
	int t; cin>>t;
	while (t--){
		cin>>n>>k; 
		int x=(n+k-1)/k,id=0,now=0;
		for (int i=1; i<=n; i+=k){
			int cnt,x=1;
			if (i+k>n){
				cnt=n-i+1;
			}
            else cnt=k;
			int mid=(cnt-1)/2;
			for (int j=0; j<cnt; j++){
				ans[i+j]=id+(mid-j-1);
				if (ans[i+j]<id) ans[i+j]+=cnt;
			}
			id+=cnt;
		}
        for (int i=1; i<=n; i++) 
            cout<<ans[i]+1<<" ";
		cout<<"\n"<<x<<"\n";
		for (int i=1; i<=n; i++){
			if ((i-1)%k==0) now++;
			cout<<now<<" ";
		}
        cout<<"\n";
	}
	return 0;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值