NOI.AC 运气大战

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/83688519

运气大战
你的班上n个同学要去参加一项集体比赛。每个人有实力值和运气值。每个人的实力值是确定的,但是运气值是飘忽不定的。一个人的发挥是他的实力值wi和运气值的乘积,即wi⋅rci。班级的发挥是所有人发挥之和。每个人有一个初始运气值ri,但是每次比赛的时候,每个人的运气值是所有人运气值的一个排列,并且要满足,排列之后ii的运气值不是ri。即满足,ii的运气值是rci,{ci}是1−n1−n的排列,且满足ci≠i。

现在有Q场比赛,每场比赛前会交换两人的初始运气值。问你每场比赛班级可以获得的最大发挥是多少。
n<=30000

由于排序不等式,我们尽量想顺序放。两边都排序。
由于 n 个不能配的干扰,又不能完全顺序放。
有个结论,最后匹配出第 i 个人的运气值是第 j 个的话,∣i−j∣≤2 。这个结论从最小化逆序对的个数来看,自己把附近几个线连起来画一画证明一下。

这样就可以用 dp[i]表示到 i 为止所有配好的最优答案。计算的时候需要用到前三轮的答案然后讨论一下。这个是 O(nq)的,可以过70%。

用线段树记录区间答案。区间记录这样的信息:把这个区间前0-2个和后0-2个元素去掉的答案,用3x3的矩阵维护。这样复杂度是O(qlogn)。
对于线性递推式可以用矩阵优化,这个可以用矩阵维护dp。

由于出题人良心发现将时限改为3s,常数小的nq算法也能过1.5s左右.
AC Code:

#include<bits/stdc++.h>
#define maxn 30005
#define LL long long
using namespace std;

int n,q,w[maxn],r[maxn],c1[maxn],c2[maxn];
inline bool cmp1(const int &u,const int &v){ return w[u] > w[v]; }
inline bool cmp2(const int &u,const int &v){ return r[u] > r[v]; }
int loc[maxn];

const LL inf = -1ll<<60;
inline LL calc(int a,int b)
{
	if(a<=0 || b<=0 || a>n || b>n) return inf;
	return 1ll * w[c1[a]] * r[c2[b]];
} 

LL dp[maxn];
LL cst[maxn][2];
inline void upd(int x)
{
	if(x<=0 || x>n) return;
	cst[x][0] = calc(x-1,x) + calc(x,x-1);
	cst[x][1] = max(
		calc(x-2,x-1)+calc(x-1,x)+calc(x,x-2),
		calc(x,x-1)+calc(x-1,x-2)+calc(x-2,x)
	);
}

int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]),c1[i]=i;
	for(int i=1;i<=n;i++) scanf("%d",&r[i]),c2[i]=i;
	sort(c1+1,c1+n+1,cmp1);
	sort(c2+1,c2+n+1,cmp2);
	for(int i=1;i<=n;i++) loc[c2[i]] = i , upd(i);
	for(int u,v;q--;)
	{
		scanf("%d%d",&u,&v);
		swap(r[u],r[v]),swap(loc[u],loc[v]);
		c2[loc[u]] = u, c2[loc[v]] = v;
		for(int i=0;i<3;i++)
			upd(c1[u]+i),upd(c1[v]+i),upd(c2[u]+i),upd(c2[v]+i);
		for(int i=1;i<=n;i++)
			dp[i] = max((i>1 ? dp[i-2]:0) + cst[i][0] , (i>2?dp[i-3]:0) + cst[i][1]),
			(c1[i]!=c2[i]) && (dp[i] = max(dp[i] , dp[i-1] + calc(i,i))); 
		printf("%lld\n",dp[n]);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/83688519