flandre

给定一个长度为 \(n\) 数列 \(a\),你可以按照一定顺序选择它们中的一个或多个。

初始贡献为你选择的数之和。

设新的数列是长度为 \(m\) 的数列 \(b\)。对于选择的每个数 \(b_i\),会造成 \(\sum_{j=i+1}^mget(a_j,a_i)\) 的贡献,\(get(x,y)\) 的定义如下:

\(x>y\)\(k\)

\(x<y\)\(-k\)

\(x=y\)\(0\)

求最大的贡献。

可以发现逆序对会造成负的贡献,所以选择的数一定是升序的。

如果不考虑重复元素,可以发现选择的数一定是一段完整的后缀(如果不是,可以恰好移到贴合)。

如果有重复元素,可以将相同的元素看作一个元素,如果某一段选择包含某个数,前面也可以选择,还可以使得 \(k\) 的累加次数更多。

所以可以考虑利用桶排方便的统计后面的数的个数(对于所有数作 \(+2e6\) 的偏移),然后在所有后缀答案中求出最大的。

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N=1000010,M=2000010,B=1e6;
void read(int &x){
	char c=getchar();
	x=0;
	bool f=0;
	while(c<'0'||c>'9')f|=c=='-',c=getchar();
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	f&&(x=-x);
}
int n,k,c[M],id=-1,h[M],idx,e[M],ne[M],maxn,minn=1e9,id2;
typedef long long ll;
char num[30];
ll ans,now,res;
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void write(ll x,char fg=' '){
	int cur=0;
	while(x)num[cur++]=x%10+'0',x/=10;
	while(cur--)putchar(num[cur]);
	putchar(fg);
}
int main(){
	memset(h,-1,sizeof h);
	read(n),read(k);
	for(int i=1;i<=n;++i){
		int x;
		read(x);
		++c[x+B];
		add(x+B,i);
		maxn=max(maxn,x+B);
		minn=min(minn,x+B);
	}
	for(int i=maxn;i>=minn;--i){
		ans+=c[i]*(i-B)+now*c[i]*k;
		now+=c[i];
		if(ans>res){
			id=i,id2=now;
			res=ans;
		}
	}
	if(id==-1){
		puts("0 0");
		return 0;
	}
	write(res),write(id2,'\n');
	for(int j=id;j<M;++j)
		for(int i=h[j];~i;i=ne[i])
			write(e[i]);
	return 0;
}
posted @ 2023-11-17 12:57  wscqwq  阅读(3)  评论(0编辑  收藏  举报