把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【YbtOJ461】「数据结构优化 DP」区间覆盖(线段树优化DP)

点此看题面

  • 给定数轴上\(n\)个区间和\(m\)个点。
  • 求有多少种选择区间的方式,满足能覆盖所有点。
  • \(n,m\le5\times10^5\)

线段树优化\(DP\)

考虑先把所有区间按左端点排序,然后维护\(f_{i,j}\)表示处理到第\(i\)个区间,最大右端点为\(j\)的概率。

假设第\(i\)个区间左端点右侧第一个点编号为\(l\),右端点右侧第一个点为\(r\)

那么\(f_{i-1,j}(j\in[l-1\sim r-1])\)都可以转移到\(f_{i,r}\)\(f_{i-1,j}(j\in[r,m])\)都可以转移到对应的\(f_{i,j}\)

其实也就是每次对\([l-1,r-1]\)区间求和然后对\(r\)单点修改,并对\([r,m]\)区间乘\(2\)

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define X 1000000009
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,p[N+5],dc,dv[2*N+5],pos[2*N+5],f[2*N+5],g[2*N+5];
struct Il
{
	int l,r;I Il(CI a=0,CI b=0):l(a),r(b){}
	I bool operator < (Con Il& o) Con {return l<o.l;}
}s[2*N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
class SegmentTree
{
	private:
		#define PT CI l=0,CI r=m,CI rt=1
		#define LT l,mid,rt<<1
		#define RT mid+1,r,rt<<1|1
		#define PU(x) (V[x]=(V[x<<1]+V[x<<1|1])%X)
		#define PD(x) F[x]^1&&(T(x<<1,F[x]),T(x<<1|1,F[x]),F[x]=1)
		#define T(x,v) (V[x]=1LL*V[x]*v%X,F[x]=1LL*F[x]*v%X)
		int V[N<<2],F[N<<2];
	public:
		I void Init() {memset(F,1,sizeof(F));}
		I void U(CI x,CI v,PT)//单点修改
		{
			if(l==r) return (void)Inc(V[rt],v);RI mid=l+r>>1;PD(rt);
			x<=mid?U(x,v,LT):U(x,v,RT),PU(rt);
		}
		I void M(CI L,CI R,PT)//区间乘2
		{
			if(L<=l&&r<=R) return (void)T(rt,2);RI mid=l+r>>1;PD(rt);
			L<=mid&&(M(L,R,LT),0),R>mid&&(M(L,R,RT),0),PU(rt);
		}
		I int Q(CI L,CI R,PT)//区间求和
		{
			if(L<=l&&r<=R) return V[rt];RI mid=l+r>>1;PD(rt);
			return ((L<=mid?Q(L,R,LT):0)+(R>mid?Q(L,R,RT):0))%X;
		}
}S;
int main()
{
	freopen("xmasinterval.in","r",stdin),freopen("xmasinterval.out","w",stdout);
	RI i;for(F.read(n),F.read(m),i=1;i<=n;++i)
		F.read(s[i].l),F.read(s[i].r),dv[i]=s[i].l,dv[n+i]=++s[i].r;//改成左闭右开区间
	for(sort(dv+1,dv+2*n+1),dc=unique(dv+1,dv+2*n+1)-dv-1,i=1;i<=n;++i)
		s[i].l=lower_bound(dv+1,dv+dc+1,s[i].l)-dv,s[i].r=lower_bound(dv+1,dv+dc+1,s[i].r)-dv;//离散化
	for(i=1;i<=m;++i) F.read(p[i]),p[i]=upper_bound(dv+1,dv+dc+1,p[i])-dv-1;
	for(sort(p+1,p+m+1),p[m+1]=dc,i=m+1;i;--i) pos[p[i]]=i;for(i=dc;i;--i) !pos[i]&&(pos[i]=pos[i+1]);//求出每个位置右边第一个点编号
	for(sort(s+1,s+n+1),S.Init(),S.U(0,1),i=1;i<=n;++i)//按左端点排序后枚举区间
		S.U(pos[s[i].r]-1,S.Q(pos[s[i].l]-1,pos[s[i].r]-1)),pos[s[i].r]<=m&&(S.M(pos[s[i].r],m),0);//线段树优化DP
	return printf("%d\n",S.Q(m,m)),0;//单点询问得出答案
}
posted @ 2021-02-07 11:45  TheLostWeak  阅读(13)  评论(0编辑  收藏  举报