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}\) 的最小值下标。
不过,我们还要讨论两个细节:
-
如果最小值有多个,那么要交换哪个。容易想到的是交换最后面那个,因为这样会将较大的 \(a_i\) 放在后面,而较小的 \(a_k\) 则在前面,如果 \(a_i\) 不是最后一个最小值 \(a_k\),哪怕从 \(i+1\) 开始可以有更大的发展前途(可以交换的数更多),但是根据字典序的定义,是优先比较前面的数,前面就已经不是最小的数,压根比不到后面去。
-
如果该数已经是最小值,是否要与后面的和它同样小的值交换?答案是不要。首先假设这是最后一次交换,答案字典序并不会更优(和原来一样),而如果这不是最后一次交换,我们选择不交换,可以比交换更多的考虑 \(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;
}

浙公网安备 33010602011771号