题目大意:有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;
}