题解 P3488 【[POI2009]LYZ-Ice Skates】
这题好妙啊。
首先不看数据范围是二分图匹配的裸题。但显然会爆炸。
从暴力的做法入手,进行思考。这里引用一个Hall定理:对于一个二分图,设左边有个n点,右边有个m点,则左边个点能完全匹配的充要条件是:对于1<=i<=n,左面任意i个点,都至少有i个右面的点与它相连。
要满足如上条件,从最劣情况入手,显然选择连续型号的人会使右边与他相连的人最少。设a[i]为选择i这个型号的人的数量,sum[l,r]为a[l..r]的总和。
那么对于任意的sum[l,r]<=(r-l+1+d)*k
可以换一下形式。
设s[l,r]为l<=i<=r, a[i]-k的和。
那么对于任意的l,r,s[l,r]<=d*k
于是这题就变成动态维护最大子段和了,线段树即可。
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){int x=0,f=1,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}
const int N=2e5+5;
struct node{
int lmx,rmx,sum,ans;
}a[N<<2];
int n,m,K,d;
inline void pushup(int k){
a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
a[k].lmx=max(a[k<<1].lmx,a[k<<1].sum+a[k<<1|1].lmx);
a[k].rmx=max(a[k<<1|1].rmx,a[k<<1|1].sum+a[k<<1].rmx);
a[k].ans=max(a[k<<1].ans,max(a[k<<1|1].ans,a[k<<1].rmx+a[k<<1|1].lmx));
}
void build(int k,int l,int r){
if (l==r){
a[k]=(node){0,0,-K,-K};
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid); build(k<<1|1,mid+1,r);
pushup(k);
}
void update(int k,int l,int r,int x,int y){
if (l==r){
a[k].ans+=y; a[k].sum+=y;
a[k].lmx=a[k].rmx=max(a[k].sum,0ll);
return;
}
int mid=(l+r)>>1;
if (mid>=x) update(k<<1,l,mid,x,y);
else update(k<<1|1,mid+1,r,x,y);
pushup(k);
}
inline void init(){
n=read(); m=read(); K=read(); d=read();
build(1,1,n);
}
inline void solve(){
for (int i=1;i<=m;i++){
int x=read(),y=read();
update(1,1,n,x,y);
if (a[1].ans<=K*d) puts("TAK"); else puts("NIE");
}
}
signed main(){
init();
solve();
return 0;
}