牛客多校第四场 H subseq

这场树状数组用法专场,skydec说这题很套路的题,很简单,然而处理处dp数组后最后的贪心我想了半天还问了一蛤别人,菜不成声.jpg,先处理出dp数组,dp[i]表示以a[i]为开头的从i到n的所有递增子序列的方案数,本来应该用线段树了,然而好多人使用了神奇的树状数组,以前都是add函数从i到cnt,sum函数从i到0,但这里的方案数是会超过1e18的,那么树状数组中的值大于1e18的时候立刻赋值为1e18,那么如果还是sum从i到0的话,这里在计算dp[i]的时候dp[i]=sum(cnt)-sum(a[i]+1),因为大于1e18赋值的时候是会出错的,所以这里的add函数变成从i到0,sum函数变成从i到cnt,这样的树状数组竟然也可以!涨见识了

预处理出dp[i]接下来就是贪心求第k大的方案了,从i=1-->n按位考虑,如果当前剩余k<=dp[i],那么a[i]必须是答案中的,因为如果第i位不选,那么以a[i]为开头的子序列个数将大于等于k,所以a[i]必定是大小等于k的子序列中的一个元素,此时总第k大序列少了a[i]这一个单元素序列,k--,而如果k>dp[i],则如果在答案中加入a[i],再去后面找递增序列,怎么找也找不到第k大,所以a[i]不可能在答案序列中,而且此时以a[i]为开头的所有递增子序列再加上前面已加入答案的子序列字典序一定是要比答案小的,所以此时k-=dp[i],这个地方我想了很久没想清楚,问了一波tsz,假设当前答案中已经加入了1,2 两个元素,此时3号元素dp[i]要比k大,我们考虑dp[i]的问题,他是从后往前推出的数组,如果3号元素要比1号元素大,那么k在1号元素的地方就已经把3号元素考虑过了,因为dp[1]的方案数是包括dp[3]的,此时把这一位后面的所有情况都加上,还是达不到k,那么字典序就全比k小,如果3号元素比1号元素小,那就没什么问题了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxl 500010

using namespace std;

const long long inf=1e18+1;

int n,cnt;
int a[maxl];
struct node
{
	int num,id;
}s[maxl];
long long k;
long long b[maxl],dp[maxl];
int ans[maxl];

inline bool cmp(const node &x,const node &y)
{
	return x.num<y.num;
}

inline void prework()
{
	memset(b,0,sizeof(int)*(cnt+1));
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&s[i].num);
		s[i].id=i;
	}
	sort(s+1,s+1+n,cmp);
	cnt=1;a[s[1].id]=1;
	for(int i=2;i<=n;i++)
	if(s[i].num!=s[i-1].num)
		++cnt,a[s[i].id]=cnt;
	else
		a[s[i].id]=cnt;
}

inline void add(int i,long long x)
{
	while(i)
	{
		b[i]+=x;
		if(b[i]>inf) b[i]=inf+1;
		i-=i&-i;
	}
}

inline long long sum(int i)
{
	long long s=0;
	while(i<=cnt)
	{
		s+=b[i];
		if(s>inf)
			return inf+1;
		i+=i&-i;
	}
	return s;
}

inline void mainwork()
{
	for(int i=n;i>=1;i--)
	{
		dp[i]=sum(a[i]+1)+1;
		add(a[i],dp[i]);
	}
	ans[0]=0;
	for(int i=1;i<=n;i++)
	{
		if(k<=0) break;
		if(a[i]>a[ans[ans[0]]])	
		{
			if(k<=dp[i]) 
				ans[++ans[0]]=i,k--;
			else
				k-=dp[i];
		}
	}
}

inline void print()
{
	if(k>0)
		printf("-1\n");
	else
	{
		printf("%d\n",ans[0]);
		for(int i=1;i<=ans[0];i++)
			printf("%d%c",ans[i],i==ans[0]?'\n':' ');
	}
}

int main()
{
	while(~scanf("%d%lld",&n,&k))
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/81475959
今日推荐