[构造]Make Nonzero Sum Codeforces1754C

11 篇文章 0 订阅

You are given an array [a1,a2,…an][a1,a2,…an] consisting of integers −1−1 and 11. You have to build a partition of this array into the set of segments [l1,r1],[l2,r2],…,[lk,rk][l1,r1],[l2,r2],…,[lk,rk] with the following property:

  • Denote the alternating sum of all elements of the ii-th segment as sisi: sisi = ali−ali+1+ali+2−ali+3+…±ariali−ali+1+ali+2−ali+3+…±ari. For example, the alternating sum of elements of segment [2,4][2,4] in array [1,0,−1,1,1][1,0,−1,1,1] equals to 0−(−1)+1=20−(−1)+1=2.
  • The sum of sisi over all segments of partition should be equal to zero.

Note that each sisi does not have to be equal to zero, this property is about sum of sisi over all segments of partition.

The set of segments [l1,r1],[l2,r2],…,[lk,rk][l1,r1],[l2,r2],…,[lk,rk] is called a partition of the array aa of length nn if 1=l1≤r1,l2≤r2,…,lk≤rk=n1=l1≤r1,l2≤r2,…,lk≤rk=n and ri+1=li+1ri+1=li+1 for all i=1,2,…k−1i=1,2,…k−1. In other words, each element of the array must belong to exactly one segment.

You have to build a partition of the given array with properties described above or determine that such partition does not exist.

Note that it is not required to minimize the number of segments in the partition.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1≤t≤100001≤t≤10000). Description of the test cases follows.

The first line of each test case contains an integer nn (1≤n≤2000001≤n≤200000) — the length of the array aa.

The second line of each test case contains nn integers a1,a2,…,ana1,a2,…,an (aiai is −1−1 or 11) — the elements of the given array.

It's guaranteed that the sum of nn over all test cases does not exceed 200000200000.

Output

For each test case, if required partition does not exist, print −1−1. Otherwise, print an integer kk — the number of segments in the partition.

Then in the ii-th of the following kk lines print two integers lili and riri — description of the ii-th segment. The following conditions should be satisfied:

  • li≤rili≤ri for each ii from 11 to kk.
  • li+1=ri+1li+1=ri+1 for each ii from 11 to (k−1)(k−1).
  • l1=1,rk=nl1=1,rk=n.

If there are multiple correct partitions of the array, print any of them.

题意: 给出长度为n的数组,在easy版本中仅由1和-1构成,在hard版本中仅有1、-1和0构成,现在可以将该数组划分成若干段,每段的贡献为a[1]-a[2]+a[3]......,问如何分段使所有段权值加和为0。

分析: 先假设所有元素各自成一段,设它们的权值和为num,可以发现无论怎么分段都会使总贡献增或减2的倍数,那么如果num值为一个奇数,很显然不可能变成0。考虑每次都构造长度为2的区间,如果num > 0,那说明贡献太大了,需要让贡献减少,有三种区间会让总贡献减少,分别是[1, 1],[-1, 1],[0, 1],并且都使总贡献减少了2,所以只需要在原数组中找到num/2个不相交的数对(x, y),满足y == 1即可,剩下的位置都各自成段即可。对于num < 0的情况类似,不过是找num/2个数对(x, y),满足y == -1。

接下来说下为什么一定能够找到num/2个这样的数对,因为num等于1的个数减去-1的个数,所以原数组中1的个数一定大于等于num,在最坏的情况下所有的1都连续挨着,但即使在这种情况下也显然有num/2个数对,所有符合要求的数对个数一定大于等于num/2。

最后注意输出的时候按照顺序输出即可。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;

int a[200005];
bool vis[200005];

void solve(){
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) vis[i] = false;
	int num = 0;
	for(int i = 1; i <= n; i++){
		scanf("%d", &a[i]);
		num += a[i];
	}
	if(num&1){
		puts("-1");
		return;
	}
	int t = 0;
	vector<int> b;
	if(num > 0){
		for(int i = 2; i <= n; i++){
			if(a[i] == 1){
				t++;
				b.push_back(i);
				i++;
				if(t == num/2) break;
			}
		}
	}
	if(num < 0){
		for(int i = 2; i <= n; i++){
			if(a[i] == -1){
				t++;
				b.push_back(i);
				i++;
				if(t == (-num)/2) break;
			}
		}
	}
	printf("%d\n", n-t);
	for(int i = 0; i < b.size(); i++){
		vis[b[i]-1] = true;
	}
	for(int i = 1; i <= n; i++){
		if(!vis[i]) 
			printf("%d %d\n", i, i);
		else{
			printf("%d %d\n", i, i+1);
			i++;
		}
	}
} 

signed main()
{
	int T;
	cin >> T;
	while(T--){
		solve(); 
	}
	return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值