[cf720D]Slalom
对于每一行,这些障碍将其划分为若干段,记第$i$行($y=i$时)从左到右第$j$段为$[l_{i,j},r_{i,j}]$
显然一条路径恰好经过每一行中的一段,且两种方案不同当且仅当其中经过的一段不同
对于某一条路径,令$a_{i}$为其经过第$i$行时的段,则合法当且仅当
$$
a_{1}=1且\forall 2\le i\le n,[\max_{j=1}^{i-1}l_{j,a_{j}},r_{i-1,a_{i-1}}]\cap [l_{i,a_{i}},r_{i,a_{i}}]\ne \empty
$$
且两种方案不同当且仅当$a_{i}$不同,题目即变为统计$a_{i}$的方案
考虑dp统计,令$f_{i,j}$表示仅考虑$a_{1},a_{2},...,a_{i}$,满足$\max_{k=1}^{i}l_{k,a_{k}}=j$且合法(仅考虑确定的部分)的方案数,初始状态由于强制$a_{1}=1$,即$f_{1,1}=1$
同时有交的条件,也就保证了$\max_{j=1}^{i}l_{j,a_{j}}$恰好在$a_{i}$所在段中,即通过$j$即可确定$a_{i}$
由此,就可以得到暴力转移的式子,即
$$
f_{i,j}=\begin{cases}0&(a_{i}不存在)\\
f_{i-1,j}&(l_{i,a_{i}}\ne j)\\\sum_{j'=l_{i-1,a_{i-1}}}^{j}f_{i-1,j'}&(l_{i,a_{i}}=j)\end{cases}
$$
(其中$a_{i}$和$a_{i-1}$分别为第$i$和$i-1$行包含$j$的段编号)
我们简单的对其更进一步的讨论,即找出所有满足$f_{i,j}=f_{i-1,j}$的情况都分入第2类,即
$$
f_{i,j}=\begin{cases}0&((a_{i}不存在)\and (a_{i-1}存在))\\
f_{i-1,j}&((a_{i}和a_{i-1}都不存在)\or (l_{i,a_{i}}\ne j)\and (l_{i-1,a_{i-1}}=j))\\\sum_{j'=l_{i-1,a_{i-1}}}^{j}f_{i-1,j'}&((l_{i,a_{i}}=j)\and (l_{i-1,a_{i-1}}\ne j))\end{cases}
$$
考虑用线段树维护当前$f_{i-1}$这个长为$n$的序列,那么仅需要修改第1类和第3类的位置
用set来维护障碍(直接维护所有$y_{1}\le i\le y_{2}$的区间$[x_{1},x_{2}]$),差分的插入和删除
对于第1类,这些位置必然被所有新插入障碍的区间包含(虽然这些区间可能之前也有障碍,但这样的数量级已经可以保证),总次数显然为$o(k)$
对于第3类,也必然是新插入的障碍的$x+1$的位置(其中$x$为插入障碍的右端点)且未被障碍覆盖,在set中找到左端点小于等于$x+1$中最大的的右端点$x'$,对$[x'+1,x+1]$求和作为$f_{i,x+1}$即可
(特别的,当不存在这样的区间时,令$x'$为0)
但找到$x'$事实上比较困难,可能需要手写平衡树,注意到被障碍覆盖的位置$f$一定是0,因此$x'$只需要满足$[x'+1,x+1]$这一段仅有一个后缀(可以为空)无障碍即可
那么只需要选择左端点小于等于$x$中最大的左端点所对应的右端点作为$x'$即可
(另外需要记录答案统一修改,防止修改和查询之间相互影响)
最终的答案不是$\sum_{i=1}^{n}f_{m,i}$,而是要对找到右端点最大的障碍来统计,同样的原因也可以选左端点最大的右端点来计算
总复杂度即为$o(m+k\log n)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1000005 4 #define mod 1000000007 5 #define L (k<<1) 6 #define R (L+1) 7 #define mid (l+r>>1) 8 #define fi first 9 #define se second 10 vector<pair<int,int> >v,v_add[N],v_del[N]; 11 multiset<pair<int,int> >s; 12 multiset<pair<int,int> >::iterator it; 13 int n,m,k,f[N<<2],tag[N<<2]; 14 void upd(int k){ 15 tag[k]=1; 16 f[k]=0; 17 } 18 void up(int k){ 19 f[k]=(f[L]+f[R])%mod; 20 } 21 void down(int k){ 22 if (tag[k]){ 23 upd(L); 24 upd(R); 25 tag[k]=0; 26 } 27 } 28 void update_val(int k,int l,int r,int x,int y){ 29 if (l==r){ 30 f[k]=y; 31 return; 32 } 33 down(k); 34 if (x<=mid)update_val(L,l,mid,x,y); 35 else update_val(R,mid+1,r,x,y); 36 up(k); 37 } 38 void update_zero(int k,int l,int r,int x,int y){ 39 if ((l>y)||(x>r))return; 40 if ((x<=l)&&(r<=y)){ 41 upd(k); 42 return; 43 } 44 down(k); 45 update_zero(L,l,mid,x,y); 46 update_zero(R,mid+1,r,x,y); 47 up(k); 48 } 49 int query(int k,int l,int r,int x,int y){ 50 if ((l>y)||(x>r))return 0; 51 if ((x<=l)&&(r<=y))return f[k]; 52 down(k); 53 return (query(L,l,mid,x,y)+query(R,mid+1,r,x,y))%mod; 54 } 55 int main(){ 56 scanf("%d%d%d",&n,&m,&k); 57 update_val(1,1,n,1,1); 58 for(int i=1;i<=k;i++){ 59 int x1,y1,x2,y2; 60 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 61 v_add[y1].push_back(make_pair(x1,x2)); 62 if (y2<m)v_del[y2+1].push_back(make_pair(x1,x2)); 63 } 64 for(int i=1;i<=m;i++){ 65 v.clear(); 66 for(int j=0;j<v_add[i].size();j++){ 67 int x=v_add[i][j].se+1; 68 if ((i>1)&&(x<=n)){ 69 it=s.lower_bound(make_pair(x+1,0)); 70 int xx=1; 71 if (it!=s.begin())xx=(*--it).se+1; 72 if (xx<x)v.push_back(make_pair(x,query(1,1,n,xx,x))); 73 } 74 } 75 for(int j=0;j<v.size();j++)update_val(1,1,n,v[j].fi,v[j].se); 76 for(int j=0;j<v_add[i].size();j++){ 77 s.insert(v_add[i][j]); 78 update_zero(1,1,n,v_add[i][j].fi,v_add[i][j].se); 79 } 80 for(int j=0;j<v_del[i].size();j++)s.erase(s.find(v_del[i][j])); 81 } 82 if (s.size())printf("%d",query(1,1,n,(*--s.end()).se+1,n)); 83 else printf("%d",query(1,1,n,1,n)); 84 }