[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)$,可以通过

[cf720D]Slalom
 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 } 
View Code

 

上一篇:智能化CRM客户关系管理系统介绍一


下一篇:哈希表