P1667 数列(置换&前缀和)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1667 数列(置换&前缀和)相关的知识,希望对你有一定的参考价值。

P1667 数列(置换&前缀和)

考虑对前缀和操作。
s l − 1 + = x s l = s l + x − x s r − = x s r + 1 = s r + 1 + x − x \\large s_{l-1}+=x \\\\ \\large s_l = s_l +x -x \\\\ \\large s_{r}-=x \\\\ \\large s_{r+1}= s_{r+1} + x-x sl1+=xsl=sl+xxsr=xsr+1=sr+1+xx
s r − s l − 1 = x \\large s_r-s_{l-1}=x srsl1=x

现在 s l − 1 − s r = x \\large s_{l-1}-s_r=x sl1sr=x,其他未变。

所以相当于交换操作。

题意转换为交换最少次数变成递增序。

根据置换的知识,答案就是: n − m n-m nm​。

为什么是 m m m呢?,因为一个长为 x x x的置换环最少只需要 x − 1 x-1 x1次,所以多一个置换环相当于减少一次交换次数。

m m m是置换环的个数。

也可以贪心找。先离散化下,预处理第 i i i小的前缀和所在的位置 c [ i ] c[i] c[i]​,然后每次贪心就可以了。

或者也可以搜索,找到环就就是一个置换环。

// Problem: P1667 数列
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1667
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-23 20:21:23
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
ll a[N],b[N],p[N],c[N];
int n;
bool cmp(int x,int y){
	return a[x]<a[y];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]),a[i]+=a[i-1],b[i]=a[i];p[i]=i;
	}
	sort(b+1,b+n+1);
	if(b[1]<=0) return puts("-1"),0;
	for(int i=2;i<=n;i++) if(b[i]==b[i-1]) return puts("-1"),0;
	sort(p+1,p+n+1,cmp);
	for(int i=1;i<=n;i++) c[p[i]]=i;
	int ans=0;
	for(int i=1;i<=n;i++)
		while(c[i]!=i) swap(c[i],c[c[i]]),ans++;
	printf("%d\\n",ans);
	return 0;
}

以上是关于P1667 数列(置换&前缀和)的主要内容,如果未能解决你的问题,请参考以下文章

排列/置换的最小前缀

[置换 前缀和]牛牛的猜球游戏

1049 数列的片段和

前缀和&差分

Prefix Sum & Difference

PTA乙级 (1049 数列的片段和 (20分))