uoj 246. 【UER #7】套路

#246. 【UER #7】套路
反攻正在进行中,按照套路,跳蚤国将会很快获得最终的胜利。跳蚤国的情报局也没闲下来,他们正打算派遣一批“菲克蚤”前往跳晚国窃取有关三星 note7的资料。
Fake Yang 是这批“菲克蚤”的教练,他教会他们各种 Fake的技术,以便更好混入敌方内部。共 n只菲克蚤,由 1到 n编号。Fake Yang给每个菲克蚤都算了特征值 a1,…,an,两个菲克蚤的相似度定义成这两个菲克蚤的特征值的差的绝对值,即第 i只菲克蚤与第 j只菲克蚤的相似度为∣ai−aj∣。
现在这批菲克蚤排成一列在 Fake Yang 面前,Fake Yang 需要在其中选出一些菲克蚤合成一个行动小队。按照套路,他会选取连续一整段的菲克蚤 al,al+1,…,ar。很显然,这个行动小队越大越好,但是按照套路,小队内的跳蚤最好都各不相同,假如有两只跳蚤长得很像的话很可能会引起跳晚们的怀疑。为此 Fake Yang 将小队的相似度定义为小队中的跳蚤两两之间的最小的相似度,用 s(l,r) 表示。
为保证安全,现在他想选取至少 k只跳蚤,且使得安全值最大。其中安全值定义如下:s(l,r)×(r−l)
但是,他并不知道最优解是什么,于是按照套路你需要帮助他求得这个值。
输入格式
按照套路,第一行三个正整数 n,m,k。k 的意义如前所述,n表示跳蚤的只数。
接下来一行 n个整数,按照套路依次表示 n只跳蚤的特征值 a1,…,an,保证 1≤ai≤m。
输出格式
按照套路,一行一个整数,表示答案。
input
10 10 2
1 4 2 6 1 9 6 8 10 3
output
8
explanation
一种方案是选取区间 [5,6],相似度为 8,答案为8×(6−5)=8。
限制与约定
由于一些原因,本题我们需要按照套路使用捆绑测试。每个子任务有若干个测试点,分为 5个子任务,你只有通过一个子任务的所有测试点才能按照套路得到这个子任务的分数。
子任务 分值 n的规模 m的规模
1 7 n≤100 m≤50000
2 13 n≤5000
3 20 n≤50000 m≤1000
4 30 m≤50000
5 30 n≤200000 m≤200000
在所有数据中,满足 2≤n≤200000,1≤m≤200000,2≤k≤n。
时间限制:2s
空间限制:256MB


题解:

算法一:dp

f[i][j]= abs(a[i]-a[j]) i+1=j

         min(abs(a[i]-a[j]),min(f[i][j-1],f[i+1][j])) j-i>1

f[i][j]表示的是区间[i,j]的最小差值。但是数据范围很大所以我们要考虑减去一维或者滚动数组,因为当前状态i需要用到i+1的状态,所以我们倒着推。每次让当前的覆盖上一次的,上式中f[i][j]需要用到f[i+1][j]的结果现在的话直接继承。然后f[i]=min(f[i],f[i-1]),注意转换成一维后f[i]表示的是终点在i的区间。

算法二:如果一个区间的长度是x,那么最小差值一定不超过m/(x-1)

那么我们设一个常数s=sqrt(n)

当x<s时,用算法一中的算法求解。

当x>=s时,那么最小差不会超过 m/(s-1)

我们枚举差值|z-x|<=m/(s-1) ,然后找到权值z最近一次出现的位置,然后计算答案。但是这样还是不够,我们必须保证两个位置之间不存在再小的差值。所以我们还需要一个数组g[i]来记录差值i最近一次出现的位置,那么g[i]+1一定是在差值i+1或者更大的范围内,所以用(posx-g[i]-1)*(i+1)来更新答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200003
#define LL long long 
using namespace std;
LL f[N],ans,val[N];
int pos[N],n,m,k,g[N];
int main()
{
	freopen("a.in","r",stdin);
	//freopen("my.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;i++) scanf("%I64d\n",&val[i]);
	int s=floor(sqrt(n));
	memset(f,127,sizeof(f));
	for (int i=n;i>=1;i--)
	{
		for (int j=i+1;j<=min(n,i+s-1);j++){
			f[j]=min(f[j],f[j-1]);
			f[j]=min(f[j],abs(val[i]-val[j]));
			if (j-i+1>=k) ans=max(ans,(LL)(j-i)*f[j]);
		}
	}
	for (int i=1;i<=n;i++)
	{
		int t=m/s;
		for (int j=0;j<=t+1;j++)
		{
			LL y1=val[i]+j; LL y=val[i]-j;
			if (j>=1) g[j]=max(g[j-1],g[j]);
			if (y>=1) g[j]=max(g[j],pos[y]);
			if (y1<=m) g[j]=max(g[j],pos[y1]);
			if (i-g[j]+1>max(s,k))  ans=max(ans,(LL)(i-g[j]-1)*(LL)(j+1));
		}
		pos[val[i]]=i;
	}
	printf("%I64d\n",ans);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值