[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++

posted @ 2019-10-26 17:07  HEOI-动动  阅读(182)  评论(0编辑  收藏  举报