[BZOJ 1135][POI2009]Lyz

题意

初始时滑冰俱乐部有 \(1\) 到 \(n\) 号的溜冰鞋各 \(k\) 双。已知 \(x\) 号脚的人可以穿 \(x\) 到 \(x+d\) 的溜冰鞋。

有 \(m\) 次操作,每次包含两个数 \(r_i,x_i\) 代表来了 \(x_i\) 个 \(r_i\) 号脚的人。\(x_i\) 为负,则代表走了这么多人。 对于每次操作,输出溜冰鞋是否足够。足够输出 TAK, 否则输出 NIE.

\(n\le 2\times 10^5,m\le5\times 10^5,k\le1\times 10^9,d\in[0,n],1\le r_i\le n-d,|x_i|\le1\times 10^9\)

题解

被某Robbery少许加强后丢到了胡策里...

由霍尔定理, 二分图存在完美匹配当且仅当一侧的任意一个子集中的点连接的另一侧的点的数量都不小于这个子集的大小. 虽然看上去要枚举子集但是实际上我们发现不连续的几段更有可能满足霍尔定理的要求, 所以只要连续区间都满足霍尔定理的要求那么所有子集就都满足了. 感性证明如下:

假设选中了一个不连续的子集, 那么显然可以在不改变另外一侧的邻接情况下在当前子集中添加新的点. 如果不能添加新的点的话可以把子集拆成若干部分, 每个部分是一个连续段. 显然拆开后或者添加新点后更可能会破坏霍尔定理的要求.

也就是说如果设 \(i\) 号脚的人共有 \(s_i\) 个, 那么溜冰鞋不足当且仅当存在任意一个区间 \([l,r]\) 满足下式:

\[\sum_{i=l}^rs_i>k(r-l+d+1)
\]

那么我们拆开移项就可以得到:

\[\begin{aligned}
\sum_{i=l}^rs_i-k(r-l+1)&>kd \\
\sum_{i=1}^r(s_i-k)&>kd
\end{aligned}
\]

于是就变成了一个支持单点加法的动态区间最大子段和问题. 线段树动态DP经典操作.

加强版里不保证 \(r\le n-d\), 需要继续考虑 \(r>n-d\) 的情况. 此时溜冰鞋不足的充要条件相当于:

\[\sum_{i=l}^rs_i>k(n-l+1)
\]

显然当 \(r=n\) 的时候左侧取到最大值, 我们只计算 \(r=n\) 时是否满足条件即可. 此时相当于:

\[\sum_{i=l}^ns_i>k(n-l+1)
\]

设 \(S\) 是 \(\langle s_i\rangle\) 的前缀和, 那么我们可以发现上式等价于:

\[\begin{aligned}
S_n-S_{l-1}&>kn-k(l-1) \\
k(l-1)-S_{l-1}&>kn-S_n
\end{aligned}
\]

左侧的最大值显然也可以用线段树维护出来.

参考代码

#include <bits/stdc++.h>

const int MAXN=1e5+10;
typedef long long intEx; struct Node{
struct Data{
intEx sum;
intEx lmax;
intEx maxs;
intEx rmax;
Data(){}
Data(intEx val){
this->sum=val;
this->lmax=this->rmax=this->maxs=std::max(this->sum,0ll);
}
Data friend operator+(const Data& a,const Data& b){
Data ans;
ans.sum=a.sum+b.sum;
ans.lmax=std::max(a.lmax,a.sum+b.lmax);
ans.rmax=std::max(a.rmax+b.sum,b.rmax);
ans.maxs=std::max(a.rmax+b.lmax,std::max(a.maxs,b.maxs));
return ans;
}
};
int l;
int r;
Data val;
Node* lch;
Node* rch;
Node(int,int);
void Maintain();
void Add(int,int);
}; int n;
int q;
int k;
int d; int main(){
scanf("%d%d%d%d",&n,&q,&k,&d);
Node* N=new Node(1,n);
for(int i=0;i<q;i++){
int p,x;
scanf("%d%d",&p,&x);
N->Add(p,x);
if(N->val.maxs>1ll*k*d)
puts("NIE");
else
puts("TAK");
}
return 0;
} Node::Node(int l,int r):l(l),r(r){
if(l==r)
this->val=Data(-k);
else{
int mid=(l+r)>>1;
this->lch=new Node(l,mid);
this->rch=new Node(mid+1,r);
this->Maintain();
}
} void Node::Add(int x,int d){
if(this->l==this->r)
this->val=Data(this->val.sum+d);
else{
if(x<=this->lch->r)
this->lch->Add(x,d);
else
this->rch->Add(x,d);
this->Maintain();
}
} inline void Node::Maintain(){
this->val=this->lch->val+this->rch->val;
}

加强版写的比较蠢...写了一个维护最大子段和的和一个区间加法区间最值的线段树...

#include <bits/stdc++.h>

const int MAXN=1e5+10;
typedef long long intEx; struct Node{
struct Data{
intEx sum;
intEx lmax;
intEx maxs;
intEx rmax;
Data(){}
Data(intEx val){
this->sum=val;
this->lmax=this->rmax=this->maxs=std::max(this->sum,0ll);
}
Data friend operator+(const Data& a,const Data& b){
Data ans;
ans.sum=a.sum+b.sum;
ans.lmax=std::max(a.lmax,a.sum+b.lmax);
ans.rmax=std::max(a.rmax+b.sum,b.rmax);
ans.maxs=std::max(a.rmax+b.lmax,std::max(a.maxs,b.maxs));
return ans;
}
};
int l;
int r;
Data val;
Node* lch;
Node* rch;
Node(int,int);
void Maintain();
void Add(int,int);
}; struct NodeX{
int l;
int r;
intEx add;
intEx max;
NodeX* lch;
NodeX* rch;
NodeX(int,int);
void PushDown();
void Maintain();
void Add(int,int,int);
void Add(const intEx&);
}; int n;
int q;
int k;
int d; int main(){
scanf("%d%d%d%d",&n,&q,&k,&d);
Node* N=new Node(1,n);
NodeX* K=new NodeX(0,n-1);
for(int i=0;i<q;i++){
int p,x;
scanf("%d%d",&p,&x);
N->Add(p,x);
if(p!=n)
K->Add(p,n-1,-x);
// printf("%lld %lld\n",N->val.maxs,K->max);
if(N->val.maxs>1ll*k*d||K->max>-N->val.sum)
puts("No");
else
puts("Yes");
}
return 0;
} NodeX::NodeX(int l,int r):l(l),r(r),add(0){
if(l==r)
this->max=1ll*l*k;
else{
int mid=(l+r)>>1;
this->lch=new NodeX(l,mid);
this->rch=new NodeX(mid+1,r);
this->Maintain();
}
} void NodeX::Add(const intEx& d){
this->add+=d;
this->max+=d;
} void NodeX::Add(int l,int r,int d){
if(l<=this->l&&this->r<=r)
this->Add(d);
else{
this->PushDown();
if(l<=this->lch->r)
this->lch->Add(l,r,d);
if(this->rch->l<=r)
this->rch->Add(l,r,d);
this->Maintain();
}
} void NodeX::PushDown(){
if(this->add){
this->lch->Add(this->add);
this->rch->Add(this->add);
this->add=0;
}
} void NodeX::Maintain(){
this->max=std::max(this->lch->max,this->rch->max);
} Node::Node(int l,int r):l(l),r(r){
if(l==r)
this->val=Data(-k);
else{
int mid=(l+r)>>1;
this->lch=new Node(l,mid);
this->rch=new Node(mid+1,r);
this->Maintain();
}
} void Node::Add(int x,int d){
if(this->l==this->r)
this->val=Data(this->val.sum+d);
else{
if(x<=this->lch->r)
this->lch->Add(x,d);
else
this->rch->Add(x,d);
this->Maintain();
}
} inline void Node::Maintain(){
this->val=this->lch->val+this->rch->val;
}

[BZOJ 1135][POI2009]Lyz的更多相关文章

  1. bzoj 1135 [POI2009]Lyz 线段树+hall定理

    1135: [POI2009]Lyz Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 573  Solved: 280[Submit][Status][ ...

  2. 1135: [POI2009]Lyz

    1135: [POI2009]Lyz https://lydsy.com/JudgeOnline/problem.php?id=1135 分析: hall定理+线段树连续区间的最大的和. 首先转化为二 ...

  3. 【BZOJ】1135: [POI2009]Lyz

    题意 有\(1\)到\(n(1 \le n \le 200000)\)号的溜冰鞋各\(k(1 \le k \le 10^9)\)双.已知\(x\)号脚的人可以穿\(x\)到\(x+d\)的溜冰鞋. 有 ...

  4. BZOJ1135: [POI2009]Lyz

    1135: [POI2009]Lyz Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 264  Solved: 106[Submit][Status] ...

  5. 【BZOJ1135】[POI2009]Lyz 线段树

    [BZOJ1135][POI2009]Lyz Description 初始时滑冰俱乐部有1到n号的溜冰鞋各k双.已知x号脚的人可以穿x到x+d的溜冰鞋. 有m次操作,每次包含两个数ri,xi代表来了x ...

  6. BZOJ 1115: [POI2009]石子游戏Kam

    1115: [POI2009]石子游戏Kam Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 883  Solved: 545[Submit][Stat ...

  7. BZOJ 1142: [POI2009]Tab

    1142: [POI2009]Tab Time Limit: 40 Sec  Memory Limit: 162 MBSubmit: 213  Solved: 80[Submit][Status][D ...

  8. bzoj 1133: [POI2009]Kon dp

    1133: [POI2009]Kon Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 242  Solved: 81[Submit][Status][D ...

  9. bzoj 1138: [POI2009]Baj 最短回文路 dp优化

    1138: [POI2009]Baj 最短回文路 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 161  Solved: 48[Submit][Sta ...

随机推荐

  1. zoj 2060 Fibonacci Again(fibonacci数列规律、整除3的数学特性)

    题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2060 题目描述: There are another kind ...

  2. 并发编程之 Fork-Join 分而治之框架

    前言 "分而治之" 一直是一个有效的处理大量数据的方法.著名的 MapReduce 也是采取了分而治之的思想.简单来说,就是如果你要处理1000个数据,但是你并不具备处理1000个 ...

  3. Spring基础(7) : Bean的名字

    1.普通bean是用id标志,context.getBean时传入名称即可获得. <bean id="p" class="com.Person"/> ...

  4. C# 利用反射将枚举绑定到下拉框

    前言:反射(Reflection)是.NET提供给开发者的一个强大工具,尽管作为.NET框架的使用者,很多时候不会用到反射.但在一些情况下,尤其是在开发一些基础框架或公共类库时,使用反射会使系统架构更 ...

  5. Java基础——详尽说明try-catch-finally的用法

    问:Java异常处理机制,理解了吗?Java异常处理,真的掌握了吗?什么是自定义异常?catch体里遇到return 是怎么处理?finally 体里有return怎么处理?catch 和 final ...

  6. Sql Server 与 MySql 在使用 update inner join 时的区别

    Sql Server -- 不使用别名 UPDATE tb_User SET tb_User.pass = '' FROM tb_User usr INNER JOIN tb_Address addr ...

  7. PHP批量导出数据为excel表格

    之前用插件phoexcel写过批量导入数据,现在用到了批量导出,就记录一下,这次批量导出没用插件,是写出一个表格,直接输出 //$teacherList 是从数据库查出来的二维数组 $execlnam ...

  8. 排序算法(9)--Distribution Sorting--分布排序[1]--Counting sort--计数器排序

    1.基本思想 假设数序列中小于元素a的个数为n,则直接把a放到第n+1个位置上.当存在几个相同的元素时要做适当的调整,因为不能把所有的元素放到同一个位置上.计数排序假设输入的元素都是0到k之间的整数. ...

  9. 正则匹配身份证有bug你知道么?

    在开发中,我们需要验证用户的输入信息,多半采用正则验证,下面就是身份证证号的几种常用的正则表达式: var  reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x) ...

  10. Atitit.播放系统规划新版本 and 最近版本回顾 v3  pbf.doc  1 版本11 (ing)41.1 规划h5本地缓存系列 41.2 Android版本app41.3 双类别系统,

    Atitit.播放系统规划新版本 and 最近版本回顾 v3  pbf.doc 1 版本11 (ing)4 1.1 规划h5本地缓存系列 4 1.2 Android版本app4 1.3 双类别系统, ...