[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 (iin[1,n]))

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

(1le n,kle 10^5,1le sle 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_Ale 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_Ale 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;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/14532009.html