1003 Range k-th Maximum Query 2020 年百度之星·程序设计大赛 - 复赛

http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=892&pid=1003

考虑求最大值的构造方法,先把a数组从小到大排序

我们思考对于第一段长度为l的,要让更大的答案覆盖更多的区间,就肯定后k个从大到小倒序放,也就是在l-k+1的位置开始放a[n],a[n-1]....a[n-k+1]放到a[l],那么第一个区间的答案就为a[n-k+1],然后这个a[n-k+1]可以一直拓展到右坐标为l+l-k的位置,然后从右坐标l+l-k+1开始,a[n]就没了,需要新增一个a数组中剩余最大的,且他就是这个区间为右端点的答案。

那么我们可以想到对于全局应该就是以 l 分段,每一段的后k个倒序从大到小,这样就能得到最大值

求最大值的答案可以把放了的地方都放上值,没放的地方都放上0,也就是这些不用管的位置小于放了的所有数字,然后滑动窗口,[l,r]->[l+1,r+1]时,只有b[l]放了,b[r+1]也放了,答案才会变成b[r+1],否则不变,这是由我们的构造方法决定的性质

求最小值就是对偶问题了,直接k=l-k+1,数组从大到小排,其他一样

#include
#define pb push_back
using namespace std;
typedef long long ll;

const int maxl=3e5+10;

ll n,len,k,ansmi,ansmx,cas;
ll a[maxl],b[maxl];
char s[maxl];
bool in[maxl]; 

inline void prework()
{
	scanf("%lld%lld%lld",&n,&len,&k);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	sort(a+1,a+1+n);
} 

inline bool cmp(const int &a,const int &b)
{
	return a>b;
}

inline void mainwork()
{
	ansmx=0;
	int now=n,l;
	for(int i=1;i<=n;i++)
		b[i]=0;
	for(int i=1;i*len-k+1<=n;i++)
		for(int j=i*len-k+1;j<=i*len && j<=n;j++)
			b[j]=a[now--];
	ll x=b[len];
	for(int r=len;r<=n;r++)
	{
		l=r-len+1;
		ansmx+=x;
		if(r==n)
			break;
		if(b[l]>0 && b[r+1]>0)
			x=b[r+1];
	}
	ansmi=0;k=len-k+1;
	now=n;
	for(int i=1;i<=n;i++)
		b[i]=0;
	sort(a+1,a+1+n,cmp);
	for(int i=1;i*len-k+1<=n;i++)
		for(int j=i*len-k+1;j<=i*len && j<=n;j++)
			b[j]=a[now--];
	x=b[len];
	for(int r=len;r<=n;r++)
	{
		l=r-len+1;
		ansmi+=x;
		if(r==n)
			break;
		if(b[l]>0 && b[r+1]>0)
			x=b[r+1];
	}
}

inline void print()
{
	printf("%lld %lld\n",ansmx,ansmi);
}

int main()
{
	int t=1;
	scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

你可能感兴趣的:(思维,规律)