P6013-[CSGRound3]压岁钱【树状数组】

正题

题目链接:https://www.luogu.com.cn/problem/P6013?contestId=25945


题目大意

n n 张牌,玩家 1 1 从顶拿若干张,之后玩家 2 2 拿若干张。

若牌的和大于 K K 那么分数为0否则为牌的和。

K K 为多少时玩家 1 1 必胜。


解题思路

我们枚举玩家 1 1 拿多少张,然后用一个指针记录玩家 2 2 拿到哪里时比玩家 1 1 大。若玩家 1 1 的和为 l l ,玩家 2 2 刚好比玩家 1 1 拿的大时的和为 r r

那么 [ l , r 1 ] [l,r-1] 这个范围都是可以选择的 K K ,用树状数组区间覆盖即可。


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
const ll N=1e6+10;
ll n,a[N],t[N],K,ans;
void Change(ll x,ll w){
	if(!x) return;
	while(x<=K){
		t[x]+=w;
		x+=lowbit(x);
	}
	return;
}
ll Ask(ll x){
	ll ans=0;
	while(x){
		ans+=t[x];
		x-=lowbit(x);
	}
	return ans;
}
int main()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	scanf("%lld",&K);
	ll l=2,z=a[1],sum=0;
	for(ll i=1;i<=n;i++){
		sum+=a[i];z-=a[i];
		while(l<=n&&z<sum)
			z+=a[l++];
		if(sum<z){
			Change(sum,1);
			Change(z,-1);
		}
		if(z<sum)
			Change(sum,1);
	}
	for(ll i=1;i<=K;i++)
		if(Ask(i)) ans++;
	printf("%lld\n",ans);
	for(ll i=1;i<=K;i++)
		if(Ask(i)) printf("%lld ",i);
}
发布了867 篇原创文章 · 获赞 55 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104106448