题目大意:
初始时滑冰俱乐部有1到n号的溜冰鞋各k双。已知x号脚的人可以穿x到x+d的溜冰鞋。 有m次操作,每次包含两个数ri,xi代表来了xi个ri号脚的人。xi为负,则代表走了这么多人。 对于每次操作,输出溜冰鞋是否足够。
思路:
感觉挺不错的一道题,果然POI的题目质量确实不错。
显然这是人和鞋子的二分图匹配,然后我们要求人这一边是否有完备匹配,那就用Hall定理了。
用Hall定理来描述这道题就是任意一个人的集合连到的鞋子的集合的点数要大于人数。
通常来看,Hall定理的题目是不可以从正面出发来判断的,因为复杂度是指数级别的,往往我们需要从反面来考虑或者使用贪心的方法来判断。
选一个尺寸的人就要把这个尺寸的人选满是没有疑问的,同时我们发现每一种尺寸对应的鞋的尺寸是一段连续的区间,那么一些尺寸的集合便对应了若干个区间的并集,那么我们所要做的就是比较这两个集合的大小关系。但是一些区间的并集也可能是不连续的,但是这里我们就把它当成连续的(也许这里才是这题最难的地方),看似是错误的,其实是对的,两个断开的集合我们把中间那一部分也算进去并不会影响答案的正确性,因为如果不合法的话会在两个断开的区间单独考虑时就判断出了。
设尺寸
的人数为
,那么我们就要判断
然后就只要用线段树来维护区间的最大连续子段和就好了。
/*=================================
* Author : ylsoi
* Algorithm : Hall & Segment_Tree
* Problem : bzoj1135
* Time : 2018.7.12
* ===============================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj1135.in","r",stdin);
freopen("bzoj1135.out","w",stdout);
}
template<typename T>T _max(T _,T __){return _>__ ? _ : __;}
const int maxn=2e5+10;
const ll inf=LLONG_MAX;
int n,m,k,d;
struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc rt<<1
#define rc rt<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
ll sum[maxn<<2],Maxl[maxn<<2],Maxr[maxn<<2],Max[maxn<<2];
void pushup(int rt){
sum[rt]=sum[lc]+sum[rc];
Maxl[rt]=_max(Maxl[lc],sum[lc]+Maxl[rc]);
Maxr[rt]=_max(Maxr[rc],sum[rc]+Maxr[lc]);
Max[rt]=_max(Maxr[lc]+Maxl[rc],_max(Max[lc],Max[rc]));
}
void build(int rt,int l,int r){
if(l==r)sum[rt]=Max[rt]=Maxl[rt]=Maxr[rt]=-k;
else build(lson), build(rson), pushup(rt);
}
void update(int rt,int l,int r,int pos,int x){
if(l==r){
sum[rt]+=x;
Max[rt]=Maxl[rt]=Maxr[rt]=sum[rt];
}
else{
if(pos<=mid)update(lson,pos,x);
else update(rson,pos,x);
pushup(rt);
}
}
}T;
int main(){
File();
scanf("%d%d%d%d",&n,&m,&k,&d);
T.build(1,1,n);
REP(i,1,m){
int u,v;
scanf("%d%d",&u,&v);
T.update(1,1,n,u,v);
if(T.Max[1]<=(ll)k*d)puts("TAK");
else puts("NIE");
}
return 0;
}