【UOJ386】【UNR #3】鸽子固定器 链表

题目描述

  有 \(n\) 个物品,每个物品有两个属性:权值 \(v\) 和大小 \(s\)

  你要选出 \(m\) 个物品,使得你选出的物品的权值的和的 \(d_v\) 次方减掉大小的极差的 \(d_s\) 次方最大。

  \(n\leq 200000,m\leq 50,1\leq d_v,d_s\leq 2\)

题解

  1. 如果选的物品的数量不到 \(m\) 个,且大小不是连续的,那么一定不是最优的。

  因为如果不是连续的,那么可以多选一个大小在这些物品之间的物品,使答案更优。

  2. 如果选了 \(m\) 个物品,那么可以枚举权值最小的物品 \(i\),可以发现,选的物品的大小组成了一个包含 \(i\) 的区间。这样的区间最多只有 \(O(m)\) 个。

  否则可以删掉 \(i\) 这个物品,换上一个权值更大的物品,使答案更优。

  可以用一个链表维护物品的大小组成的序列。

  时间复杂度:\(O(nm)\)

题解

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010;
int l[N],r[N];
int n,m,ds,dv;
ll s[N];
ll fp(ll a,int b)
{
	return b==1?a:a*a;
}
ll calc(ll x,ll y)
{
	return fp(x,dv)-fp(y,ds);
}
void del(int x)
{
	r[l[x]]=r[x];
	l[r[x]]=l[x];
}
struct pp
{
	int s,v,c;
};
pp a[N],b[N],c[N];
int cmp1(pp a,pp b)
{
	return a.s<b.s;
}
int cmp2(pp a,pp b)
{
	return a.v<b.v;
}
int t;
ll ans;
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	scanf("%d%d%d%d",&n,&m,&ds,&dv);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].s,&a[i].v);
	sort(a+1,a+n+1,cmp1);
	for(int i=1;i<=n;i++)
	{
		a[i].c=i;
		b[i]=a[i];
	}
	for(int i=1;i<=n;i++)
	{
		ll temp=0;
		for(int j=i;j<=n&&j<=i+m-1;j++)
		{
			temp+=a[j].v;
			ans=max(ans,calc(temp,a[j].s-a[i].s));
		}
	}
	sort(a+1,a+n+1,cmp2);
	for(int i=0;i<=n;i++)
		r[i]=i+1;
	for(int i=1;i<=n+1;i++)
		l[i]=i-1;
	for(int i=1;i<=n;i++)
	{
		t=0;
		for(int j=a[i].c,k=1;j>=1&&k<=m;j=l[j],k++)
			c[++t]=b[j];
		reverse(c+1,c+t+1);
		for(int j=r[a[i].c],k=2;j<=n&&k<=m;j=r[j],k++)
			c[++t]=b[j];
		for(int j=1;j<=t;j++)
			s[j]=s[j-1]+c[j].v;
		for(int j=m;j<=t;j++)
			ans=max(ans,calc(s[j]-s[j-m],c[j].s-c[j-m+1].s));
		del(a[i].c);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-07-13 16:46  ywwyww  阅读(400)  评论(0编辑  收藏  举报