Luogu8475 solution

Problem

link->https://www.luogu.com.cn/problem/P8475

Solution

贪心好题。显然要将尽量前面的位置放尽量小的数。则我们考虑 \(a_i\) 要放什么数时,理想状态下就是与 \(a_{i\sim n}\) 的最小值 \(a_k\) 交换,而且如果我们交换了 \(a_i\)\(a_k\),则下一个需要考虑的数 \(i'\) 下标一定大于 \(k\),如果 \(i'=k\) 显然不合法(对于任意一个数只能交换一次),如果 \(i'<k\),则与 \(a_i\) 交换的就不是 \(a_k\) 而是 \(a_{i'}\) 了。则我们进行一个倒序循环,\(O(n)\) 预处理处出每个 \(a_{1\sim n}\) 的最小值和最小值下标,然后进行如下操作:

p = 1
while p<=n
  swap(a[p],a[new_p])
  p = new_p+1

其中 \(new_p\)\(a_{1\sim n}\) 的最小值下标。

不过,我们还要讨论两个细节:

  1. 如果最小值有多个,那么要交换哪个。容易想到的是交换最后面那个,因为这样会将较大的 \(a_i\) 放在后面,而较小的 \(a_k\) 则在前面,如果 \(a_i\) 不是最后一个最小值 \(a_k\),哪怕从 \(i+1\) 开始可以有更大的发展前途(可以交换的数更多),但是根据字典序的定义,是优先比较前面的数,前面就已经不是最小的数,压根比不到后面去。

  2. 如果该数已经是最小值,是否要与后面的和它同样小的值交换?答案是不要。首先假设这是最后一次交换,答案字典序并不会更优(和原来一样),而如果这不是最后一次交换,我们选择不交换,可以比交换更多的考虑 \(a_{i+1\sim k}\) 这几个数(\(k\) 为如果交换则要交换的数),显然不比交换差。

然后计算即可。

Code

#include<bits/stdc++.h>
#define INF 0x7fffffff
#define inf 0x3f3f3f3f
#define inf2 0x3f3f3f3f3f3f3f3f
//#define int long long
#define PII pair<int,int>
#define _for(a,b,c) for(int a=b;a<=c;++a)
#define _rep(a,b,c) for(int a=b;a>=c;--a)
#define cl(f,x) memset(f,x,sizeof(f))
using namespace std;
const int N=1e7+5;
int n,a[N],f[N],l[N];
namespace gen {
	unsigned long long k1, k2;
	int thres;
	unsigned long long calc() {
		unsigned long long k3=k1,k4=k2;
		k1=k4,k3^=(k3<<23),k2=k3^k4^(k3>>17)^(k4>>26);
		return k2+k4;
	}
	void _gen() {
		_for(i,1,n)
			a[i]=calc()%thres;
	}
}
signed main() {
	scanf("%d",&n);
	scanf("%llu%llu%d",&gen::k1,&gen::k2,&gen::thres);
	gen::_gen();
	//	printf("gen a sequence: ");
	//	_for(i,1,n)
	//		printf("%d ",a[i]);
	//	puts("");
	cl(f,0x3f);
	_rep(i,n,1) {
		if(a[i]<f[i+1])
			f[i]=a[i],l[i]=i;
		else
			f[i]=f[i+1],l[i]=l[i+1];
	}
	int p=1;
	while(p<=n) {
		if(a[p]!=a[l[p]])
			swap(a[p],a[l[p]]),p=l[p]+1;
		else
			++p;
	}
	//	printf("new sequence: ");
	//	_for(i,1,n)
	//		printf("%d ",a[i]);
	//	puts("");
	unsigned long long ans=0;
	_for(i,1,n)
		ans+=1ll*i*a[i];
	printf("%llu\n",ans);
	return 0;
}
posted @ 2022-08-14 21:34  lsj2009  阅读(60)  评论(0)    收藏  举报