P1667 数列

数列

题目描述

给定一个长度是 n n n 的数列 A A A ,我们称一个数列是完美的,当且仅当对于其任意连续子序列的和都是正的。

现在你有一个操作可以改变数列,选择一个区间 [ l , r ] [l,r] [l,r] 满足 ∑ i = l r A i < 0 \sum\limits_{i = l}^r A_i < 0 i=lrAi<0 ,其中 1 < l ≤ r < n 1 < l \le r < n 1<lr<n

S = ∑ i = l r A i S = \sum\limits_{i = l}^r A_i S=i=lrAi ,对于 A l − 1 A_{l - 1} Al1 A r + 1 A_{r + 1} Ar+1 分别加上 S S S A l A_l Al A r A_r Ar 分别减去 S S S(如果 l = r l = r l=r 就减两次)。问最少几次这样的操作使得最终数列是完美的。

对于 100 % 100\% 100% 的数据,满足 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105 ; 1 ≤ ∣ A i ∣ < 2 31 1 \le |A_i| < 2^{31} 1Ai<231



解析:

因为有一段区间的和 S = ∑ i = x y a i S = \sum\limits_{i=x}\limits^{y} a_i S=i=xyai,所以可以考虑一下前缀和。

S = ∑ i = l r a i S = \sum\limits_{i=l}\limits^{r} a_i S=i=lrai s u m sum sum 为前缀和数组。一次操作相当于是 s u m [ l − 1 ] = s u m [ l − 1 ] + S , s u m [ r ] = s u m [ r ] − S sum[l-1] = sum[l-1] + S,sum[r] = sum[r] - S sum[l1]=sum[l1]+S,sum[r]=sum[r]S 1 < l ≤ r < n 1 < l \le r < n 1<lr<n

注意到在操作之前有 S + s u m [ l − 1 ] = s u m [ r ] S + sum[l-1] = sum[r] S+sum[l1]=sum[r],所以一次操作相当于是前缀和数组交换两个元素的位置。

连续子序列的和为正,等价于排序后的前缀和严格递增且 s u m [ 1 ] > 0 sum[1] > 0 sum[1]>0
所以,题意可以转化为:前缀和序列,任意交换两个数,变为严格单调递增的最少操作次数。

先离散化。需要交换的元素会形成一个环,一个环的最少操作次数为环上节点个数减一。可以用并查集维护是否在一个环上。

前缀和如果有元素为负,或者出现两个相等元素,无解。特别注意,操作不可以交换最后一个数,所以开始时,前缀和最后一个位置必须最大,否则也无解


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int fa[maxn], siz[maxn];
void init(int x){
	for(int i = 1; i <= x; i++){
		siz[i] = 1;
		fa[i] = i;
	}
}
int find(int x){
	return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
void merge(int x, int y){
	int fx = find(x);
	int fy = find(y);
	
	if(fx != fy){
		fa[fx] = fy;
		siz[fy] += siz[fx];
	} 
}
ll a[maxn];
struct node{
	ll val, id;
	bool operator < (const node &b) const{
		return val < b.val;
	}
}b[maxn];
set<ll> s;
ll ans, n, maxsum;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	for(int i = 1; i <= n; i++){
		b[i].val = b[i-1].val + a[i];
		b[i].id = i;
		if(b[i].val < 0 || s.count(b[i].val)){
			cout << -1 << endl;
			return 0;
		}
		s.insert(b[i].val);
		if(i == n && b[i].val <= maxsum){
			cout << -1 << endl;
			return 0;
		}
		maxsum = max(maxsum, b[i].val);
	}
	sort(b+1, b+1+n);
	init(n);
	
	for(int i = 1; i <= n; i++){
		int x = b[i].id;
		if(find(x) != find(i))
			merge(x, i);
	}	
	for(int i = 1; i <= n; i++){
		if(find(i) == i)
			ans += siz[i]-1;
	}	
	cout << ans << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用[1]中的代码,这段代码计算了一个数列的异或结果。首先,代码判断输入的数n是否为偶数,如果是偶数,则将n减1,并将结果赋给n。然后,代码计算了奇数的个数,如果奇数的个数为奇数,则将结果赋给ans,否则将0赋给ans。最后,如果n是偶数,则将ans与(n-1)进行异或运算,否则直接输出ans。这段代码的目的是计算数列的异或结果。[1] 根据引用[2]中的代码,这段代码也是计算了一个数列的异或结果。首先,代码判断输入的数n是否为偶数,如果是偶数,则将n减1,并将结果赋给n,并将even设置为true。然后,代码判断(n-1)右移1位后的结果是否为奇数,如果是奇数,则将1赋给ans,否则将0赋给ans。最后,如果n是偶数,则将ans与(n-1)进行异或运算,否则直接输出ans。这段代码的目的也是计算数列的异或结果。[2] 根据引用[3]中的代码,这段代码同样是计算了一个数列的异或结果。代码首先读取输入的数n,并计算n对4取余的结果赋给m。然后,根据m的值,分别计算不同情况下的ans的值。如果m为0,则将n赋给ans;如果m为1,则将1赋给ans;如果m为2,则将(n-1)赋给ans;如果m为3,则将0赋给ans。最后,输出ans。这段代码的目的也是计算数列的异或结果。[3] 综上所述,这些代码的目的都是计算数列的异或结果,只是实现的方式略有不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值