[CF GYM102832L] Coordinate Paper

前言

是时候多做点构造题了。

题目

CF

Vjudge

题目大意:

给定三个正整数 \(n,k,s\),要求构造一个长度为 \(n\) 的非负整数数列 \(a\),满足如下要求:

  1. \(\overset{n}{\underset{i=1}{\sum}}a_i=s\)

  2. \(a_i \ge 0 (i\in[1,n])\)

  3. \(a_i = a_{i-1}-k\) 或者 \(a_i = a_{i-1}+1(i\in[2,n])\)

\(1\le n,k\le 10^5,1\le s\le 10^{18}\)

讲解

讲解比较冗长,但请耐心看完。

part1 判断无解

首先我们需要知道一个性质:如果这个数列的第一个数定下来了,那么这个数列在模 \(k+1\) 意义下的和就定下来了。

证明显然,\(-k\)\(+1\) 在模 \(k\) 意义下等价。

而我们知道整个数列的和 \(s\),当然也就知道其在模 \(k+1\) 意义下的值,所以我们就可以根据第一个数将整个数列还原。

所以我们要先把这个数列的第一个数确定下来,我们令其为 \(A\)

但在这之前我们思考一下什么情况下无解?

令由 \(A\) 构造出来的最小的数列和为 \(S_A\)

如果 \(S_A\) 大于 \(s\),或者 \(S_A\) 在模意义下不等于 \(s\),那么一定无解。

否则我们一定可以构造出一组解。

part2 找A

思考如果知道了 \(A\),最小的数列怎么构造?

显然是这样的:\(A,A+1,A+2,...,k,0,1,...\)

对于这个数列,我们可以用等差数列求和公式 \(O(1)\) 算出 \(S_A\),而且显然 \(A \le k\),所以我们只需枚举 \(A\) 然后验证即可。

part3 构造

现在我们已经判断出了无解并求出了 \(A\),考虑构造 \(a\)

显然我们如果每个数同时加上 \(k+1\),如果 \(S_A\) 依然小于等于 \(s\),那么还是满足条件。

我们一定可以找个这么一个 \(S_A\) 使得: \(S_A\le s < S_A+n*(k+1)\)

也就是说这个时候不能再对整体加 \(k+1\) 了。

然后考虑单个加。

我们把上面我们构造出来的数列拿下来看看:\(A+w*(k+1),A+1+w*(k+1),...,k+w*(k+1),0+w*(k+1),1+w*(k+1),...\)

也就是:\(A,A+1,A+2,...,k,0,1,...\pmod{k+1}\)

显然我们可以这么构造:

\(A+(k+1),A+1,A+2,...,k,0,1,...\)(此时的 \(A\) 不是最开始求出来的 \(A\),而是 \(A+w*(k+1)\))

\(A+(k+1),A+1+(k+1),A+2,...,k,0,1,...\)

因为 \(S_A\)\(s\) 在模 \(k+1\) 意义下相等,且 \(S_A\le S<n*(k+1)\),那么我们一定可以用小于 \(n\) 次操作将 \(S_A\) 加到与 \(s\) 相等。

但是这个方法需要一点小优化,因为我们发现到加到 \(k\) 的时候不合法,也就是:

\(A+(k+1),A+1+(k+1),A+2,...,k+(k+1),0,1,...\)

最后一步了,你能想出来怎么解决吗?建议在这里暂停思考一下。

没错,我们只需要调换一下顺序就好了!

也就是先:

\(A+(k+1),A+1+(k+1),A+2,...,k,0+(k+1),1,...\)

然后:

\(A+(k+1),A+1+(k+1),A+2,...,k+(k+1),0+(k+1),1,...\)

你想到了吗?反正我没想到qwq。

时间复杂度 \(O(n+k)\)

代码

typedef long long LL;
const int MAXN = 100005;
LL n,k,s,a[MAXN],A = -1,S;

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); k = Read(); s = Read();
	for(int i = 0;i <= k && A < 0;++ i) 
	{
		//出了一万个锅的等差数列求和 
		LL t1 = Min(k - i + 1,n);
		S = (i + i+t1-1) * t1 / 2;
		S += (n-t1) / (k+1) * ((0 + k) * (k+1) / 2);
		LL fk = (n-t1) % (k+1);
		S += (0 + fk-1) * fk / 2;
		if(S <= s && S % (k+1) == s % (k+1)) A = i; 
	}
	if(A < 0)  {Put(-1);return 0;}
	a[1] = A;
	LL woc = (s - S) / (n*(k+1));//计算w 
	for(int i = 2;i <= n;++ i) a[i] = (a[i-1] + 1) % (k+1);
	for(int i = 1;i <= n;++ i) a[i] += woc * (k+1);
	S += woc * n * (k+1);
	for(int i = 1;S < s;++ i)
	{
		S += k+1;
		//判断特殊情况 
		if(a[i]+1 == a[i+1]) a[i] += k+1;
		else
		{
			a[i+1] += k+1;
			if(S == s) break;
			a[i] += k+1;
			i++;
			S += k+1;
		}
	}
	for(int i = 1;i <= n;++ i) Put(a[i],' ');
	return 0;
}
posted @ 2021-03-14 12:10  皮皮刘  阅读(54)  评论(0编辑  收藏  举报