[CSP-S模拟测试]:bird(线段树优化DP)
题目传送门(内部题89)
输入格式
第一行两个数$n$和$k$,分别表示小鸟的只数和$R$装弹时间。
接下来$n$行,每行两个数$l,r$表示$n$只小鸟初始时的头和尾的$x$坐标。
输出格式
输出一个答案表示$R$最多能得到多少只猎物。
样例
样例输入:
4 5
-1 1
2 4
5 9
6 8
样例输出:
3
数据范围与提示
对于$30\%$的数据:$n\leqslant 20,\max(|l|,|r|)\leqslant 100$。
对于$60\%$的数据:$n\leqslant 5,000,\max(|l|,|r|)\leqslant 5,000$。
对于$100\%$的数据:$n\leqslant 100,000,\max(|l|,|r|)\leqslant 500,000$。
题解
注意两次开枪之间的间隔只需要在$k$之上就好了,不需要一定是$k$。
状态转移很好写,在此不再赘述,用线段树优化即可满分。
但是需要注意的是,如果一直鸟的长度$>k$,那么就会被计数两次,若和避免这样的情况呢?
用一个数组记录当前点的鸟有多少,再在这只鸟飞走的时候再将其插入线段树,而当前点的答案就是线段树里能覆盖这个点的鸟$+$维护的那个数组的当前点的值。
因为在鸟飞走后再将其插入,所以就避免了重复计算。
注意边界问题,即$l,r<0$的情况;具体实现可以参考代码。
时间复杂度:$\Theta(n\log \max(|l|,|r|))$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
struct rec{int l,r;}pos[100001];
int n,k;
int s[500002],beg[500002],mx;
int tr[2000010],lz[2000010];
int ans;
bool cmp(rec a,rec b){return a.r==b.r?a.l<b.l:a.r<b.r;}
void pushup(int x){tr[x]=max(tr[L(x)],tr[R(x)]);}
void pushdown(int x){tr[L(x)]+=lz[x];lz[L(x)]+=lz[x];tr[R(x)]+=lz[x];lz[R(x)]+=lz[x];lz[x]=0;}
void add(int x,int l,int r,int k,int w)
{
if(l==r){tr[x]=w;return;}
int mid=(l+r)>>1;
pushdown(x);
if(k<=mid)add(L(x),l,mid,k,w);
else add(R(x),mid+1,r,k,w);
pushup(x);
}
void add(int x,int l,int r,int L,int R,int w)
{
if(r<L||R<l)return;
if(L<=l&&r<=R){tr[x]+=w;lz[x]+=w;return;}
int mid=(l+r)>>1;
pushdown(x);
add(L(x),l,mid,L,R,w);
add(R(x),mid+1,r,L,R,w);
pushup(x);
}
int ask(int x,int l,int r,int L,int R)
{
if(r<L||R<l)return 0;
if(L<=l&&r<=R)return tr[x];
int mid=(l+r)>>1;
pushdown(x);
return max(ask(L(x),l,mid,L,R),ask(R(x),mid+1,r,L,R));
}
int main()
{
scanf("%d%d",&n,&k);
int top=0;
for(int i=1;i<=n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
l=max(l,0);if(r<0)continue;
l++;r++;s[l]++;s[r+1]--;
pos[++top]=(rec){l,r};
beg[l]++;mx=max(mx,r+1);
}
n=top;sort(pos+1,pos+n+1,cmp);
for(int i=1;i<=mx;i++)s[i]+=s[i-1];
int fail=1,now=0;
for(int i=1;i<=mx;i++)
{
int flag=s[i]+ask(1,1,mx,max(0,i-(k<<1)),max(0,i-k));
now+=beg[i];ans=max(ans,flag);
add(1,1,mx,i,flag-now);
while(fail<=n&&pos[fail].r==i)
{
now--;
add(1,1,mx,pos[fail].l,pos[fail].r,1);
fail++;
}
}
printf("%d",ans);
return 0;
}
rp++