比较简单的构造。
注意到题面给出 \(a_i\le 2n-1\) 的条件,考虑这个有什么用,你会发现从 \(n\) 到 \(2n-1\) 这 \(n\) 个数都是两两互不为约数,所以当我们构造出序列后,这些数可以用来填补空位。
\(k\) 的上界是 \(\frac{n(n-1)}{2}\),显然在全部都为同一个数的时候取到,显然有 \(x\) 个数相同的时候,这 \(x\) 个数至少产生 \(\frac{x(x-1)}{2}\) 的贡献,那我们是否可以利用这个性质来构造出一个合法序列呢?当然是可以的。
首先给出这样一个性质,当我们取到的数为 \(y\) 的时候,只由一些 \(y\) 和一些 \(dy\)(\(d\) 为常数)组成的可重集 \(S\),和只由一些 \(y\) 组成的可重集 \(T\),若 \(|S|=|T|\),那么这两个产生的贡献是相等的,可以通过找规律或者灵光一现知道这件事。证明的话就是两种集合内部都是任意两种元素都可以自由握手的,所以方案数在集合大小相等时显然相等。
我们接着想,自由握手是相等的,那么再加入一个不能自由握手的元素呢?以 \(y,2y,3y\) 为例子,分别有 \(x_1,x_2\) 个 \(y,2y\),总和为 \(x\),如果加入 \(1\) 个 \(3y\),最终产生的贡献就会是 \(\frac{x(x-1)}{2}+x_1\),总和不变的情况下,将 \(x_1\) 取遍 \([0,n)\) 的话我们可以做到贡献取遍 \([\frac{x(x-1)}{2},\frac{(x+1)x}{2})\),那么由不同的 \(x\) 以及 \(x_1\) 取值,以及是否加入 \(3y\),贡献就可以取遍 \([0,\frac{n(n-1)}{2}]\),空位直接由 \(n\) 到 \(2n-1\) 中没被取的填上。
具体地就是找到最大的满足 \(\frac{x(x-1)}{2}\le k\) 的 \(x\),然后按上述构造法构造。
为了不产生额外的贡献,我们最优的取法显然是取 \(\lfloor\frac{2n-1}{3}\rfloor\) 作为 \(y\),这样是它倍数的数一般只有 \(2\) 个,可以做到与其他数不产生贡献。为什么是一般,因为显然在一种最特殊的情况下 \(n=3\) 时,\(y\) 取到 \(1\),这时就不成立,要特判一下。
实现有点细节,但不多。
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define ll long long
#define ull unsigned long long
#define m_p make_pair
#define m_t make_tuple
#define N 250010
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
using namespace __gnu_pbds;
signed main()
{
// auto __IN__ = freopen(".in", "r", stdin);
// auto __OUT__ = freopen(".out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll n, k, val, l, r, mid, ans = 0, d, p;
cin >> n >> k;
if(n==3&&k==2){
cout << "1 2 3";
return 0;
}
l = 0;
r = n;
while (l <= r)
{
mid = l + r >> 1;
if (mid * (mid - 1) / 2ll <= k)
{
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
val = (n * 2 - 1) / 3;
if (val == 1 && n == 3)
++val;
d = k - ans * (ans - 1) / 2ll;
// cerr << d << "\n";
if (ans < 2)
ans = 0;
for (int i = 1; i <= d; i++)
cout << val << " ";
for (int i = 1; i <= ans - d; i++)
cout << val * 2 << " ";
p = n;
if (ans < n && ans && ans * (ans - 1) / 2ll != k)
{
--n;
cout << val * 3 << " ";
}
for (int i = ans + 1; i <= n; i++)
{
if (k && (p == val * 3 || p == val * 2 || p == val))
++p;
cout << p << " ";
++p;
}
return 0;
}
标签:P11277,__,frac,题解,long,贡献,沉睡,2n,define
From: https://www.cnblogs.com/wryyy-233/p/18547294