2022CCPC威海 J. Eat, Sleep, Repeat (gym104023J)

Problem - J - Codeforces

题目大意:有n堆石子,每堆石子有ai个石子,P和F轮流拿一个,同时有k个限制,有x个石子的堆不能超过y个,谁拿不了谁就输,现p是先手,f后手,问谁能赢

1<=n<=1e5;0<=k<=1e5;0<=xi<=1e9;0<=yi<=n

思路:我们发现如果所有能拿的石子总数为奇数,就是先手赢,否则是后手赢,所以我们可以根据限制条件推出最终状态,从而得到能拿的石子总数。然后我们考察限制条件的规律,如果限制条件是x,0这样的,那么所有大于x的数都不能减到x,如果同时x+2也有限制的话,那么只要把正好等于x+2的数-1即可,所以所有同时存在的大于x+1的限制都是无效的,而如果同时有x+1,y2的限制,那么x+1的数就只能有y2个,以此类推,如果在有x和x+1的限制下有x+2的限制那么x+2也是有效限制,所以我们只需找到每个限制为x,0的位置,然后维护当前的最少剩余石子数。

比如对于以下样例:

5 4

6 7 8 12 17

1 1

2 1

9 0

10 1

我们先额外添加两个限制条件-1,0;1e9+1,0作为边界条件,我们单独储存所有限制条件和形如x,0的限制条件然后从第一个0限制条件开始遍历,我们令当前最小石子数应该为x+1,然后循环检查x+1是否有限制条件,没有的话只要当前的a大于x,直接向后遍历限制条件,直到a<x或遇到x,0再循环此过程

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
#define int ll
const int N = 1e5 + 5;
ll a[N],c[N];
pair<int,int>b[N];
bool cmp(pair<int, int>a, pair<int, int>b)
{
	return a.first < b.first;
}
signed main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, k;
		map<int, int>cnt;
		scanf("%lld%lld", &n, &k);
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld", &a[i]);
		}
		sort(a + 1, a + n + 1);//将石子数量从小到大排序
		int cntb = 0;
		for (int i = 1; i <= k; i++)
		{
			int x, y;
			scanf("%lld%lld", &x, &y);
			cnt[x] = y;//x的限制数量为y
			if (!y)
			{				
				b[++cntb] = make_pair(x, y);//记录x,0的限制条件
			}				
		}
		sort(b + 1, b + cntb + 1, cmp);//将0限制条件按照x从小到大的顺序排序
		b[0] = make_pair(-1, 0);
		b[cntb + 1] = make_pair(1000000005, 0);//加入边界条件
		int ita = 1, itb = 0;//a,b数组的指针
		ll ans = 0;//能拿的石子的总数
		int ma = 0;//当前最少石头数
		while (ita<=n)
		{
			while (itb <= cntb+1 && a[ita] > b[itb].first)
			{//当a>x时右移b指针,找到下一个有效0的位置,如果a是6,那3,0,4,0同时存在时3,0是无效的				
				ma = b[itb].first + 1;
				itb++;
			}
			while (ita <= n&&a[ita]<b[itb].first)
			{//找到有效0后,右移a指针,统计答案
				while (cnt[ma] && ita <= n&&a[ita]<b[itb].first)
				{//检查x+1是否存在,且a小于下一个0限制的x
					cnt[ma]--;//减计数,加答案
					ans += a[ita] - ma;
					ita++;
					if (cnt[ma] == 0)
					{//如果x+1数量减为0了,最少石子数还要+1
						ma++;
					}
				}
				if (ita > n||a[ita]>b[itb].first)
					break;//在继续统计两个0之间的答案之前必须要判断当前x是否还能限制a
				ans += a[ita] - ma;
				ita++;
			}
		}
		//cout << "ans:" << ans << endl;
		if (ans & 1)//可拿的石子数为奇数则先手赢
			printf("Pico\n");
		else
		{
			printf("FuuFuu\n");
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值