zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    快递员—你惹不起的“组织”
    BF的真正意义
    读书:《四点起床-最养生和高效的时间管理》
    你究竟有多想成功?汗水铸造巨星(中英结合版含视频)
    美国女生给我的六个惊诧(转)
    据说是世界上最健康的作息
    少走弯路的十条忠告
    编程—休息片刻的好处
    学电脑必懂的53个英文单词和缩写
    四件在我步入职业软件开发生涯那天起就该知道的事情
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14532009.html
Copyright © 2011-2022 走看看