题目链接
wls的每日一题,容斥板子。
http://oj.daimayuan.top/problem/467
题意
思路
n = 1 0 6 n = 10^6 n=106 , 显然我们不能依照 n n n来做,考虑根据障碍物 m m m进行转移。
我们记录 d p [ i ] dp[i] dp[i] 为 从 位置 ( 1 , 1 ) (1,1) (1,1)到第$ i $个障碍物的合法走法。
记第 i i i个障碍物的位置是 ( x i , y i ) (x_i,y_i) (xi,yi) ,
1.当第 i i i个障碍物前没有障碍物时,显然有:
d p [ i ] dp[i] dp[i] = C x i + y i − 2 x i − 1 C_{x_i+y_i-2}^{x_i-1} Cxi+yi−2xi−1
2.假设第 i i i个障碍物前有一个障碍物 j j j,那么我们就减去障碍物 j j j对答案的贡献:
d p [ i ] dp[i] dp[i] = d p [ i ] dp[i] dp[i] - d p [ j ] dp[j] dp[j] * (从 j j j号障碍物到 i i i号障碍物的走法)
即
d
p
[
i
]
=
d
p
[
i
]
−
∑
j
=
1
i
−
1
d
p
[
j
]
∗
C
x
i
+
y
i
−
x
j
−
y
j
x
i
−
y
i
dp[i]= dp[i] - \sum_{j=1}^{i-1} dp[j] * C_{x_i+y_i-x_j-y_j}^{x_i-y_i}
dp[i]=dp[i]−j=1∑i−1dp[j]∗Cxi+yi−xj−yjxi−yi
=
C
x
i
+
y
i
−
2
x
i
−
1
−
∑
j
=
1
i
−
1
d
p
[
j
]
∗
C
x
i
+
y
i
−
x
j
−
y
j
x
i
−
y
i
(
其
中
x
j
<
=
x
i
,
y
j
<
=
y
i
)
= C_{x_i+y_i-2}^{x_i-1} - \sum_{j=1}^{i-1} dp[j] * C_{x_i+y_i-x_j-y_j}^{x_i-y_i} ( 其中x_j<=x_i , y_j<=y_i )
=Cxi+yi−2xi−1−j=1∑i−1dp[j]∗Cxi+yi−xj−yjxi−yi(其中xj<=xi,yj<=yi)
如图 , d p [ i ] dp[i] dp[i] = 总的走法 - 蓝色区域走法*绿色区域走法
我想这个题的时候曾经有一个疑问:
“从 j j j号障碍物到 i i i号障碍物的走法” 这里可能会经过其他障碍物,不会造成重复吗?
想想其实是不会的,我们假设
j
j
j 和
i
i
i 中间插着一个障碍物
z
z
z ,
那么一定不会存在一条
j
−
−
>
z
−
−
>
i
j-->z-->i
j−−>z−−>i 的路线被统计两次,
因为
z
z
z会先于
i
i
i被访问,而
d
p
[
z
]
dp[z]
dp[z] 中一定不存在经过j的情况。
3.关于答案统计:
这个简单,创造第 n + 1 n+1 n+1个障碍物在 ( n , n ) (n,n) (n,n)处 , 答案即 d p [ n + 1 ] dp[n+1] dp[n+1]
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6+100;
const int M = 3030;
const int mod = 1e9+7;
int fac[N],inv[N];
int dp[M];
struct node{
int x;
int y;
bool operator< (node B)const{
if(x==B.x) return y<B.y;
return x<B.x;
}
};
vector<node> v;
int ksm(int a,int b,int p){
int ans = 1;
while(b){
if(b&1) ans = ans*a%p;
a = a*a%p;
b >>= 1;
}
return ans%p;
}
void pre(){
const int mx = 2e6;
fac[0] = 1;
for(int i=1;i<=mx;i++){
fac[i] = fac[i-1]*i%mod;
}
inv[mx] = ksm(fac[mx],mod-2,mod)%mod; // 逆元
for(int i=mx-1;i>=0;i--){
inv[i] = inv[i+1]*(i+1)%mod;
}
}
int C(int n,int m){
return fac[n]*inv[n-m]%mod*inv[m]%mod; // 组合数
}
signed main(){
ios::sync_with_stdio(false);
pre(); // 预处理逆元和累乘
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
v.push_back({x,y});
}
v.push_back({n,n}); // 创造第n+1个障碍物在(n,n)处
sort(v.begin(),v.end());
for(int i=0;i<v.size();i++){
node now = v[i];
dp[i] = C(now.x+now.y-2,now.x-1)%mod;
}
for(int i=0;i<v.size();i++){
node now = v[i];
for(int j=0;j<i;j++){
if(v[j].x<=v[i].x&&v[j].y<=v[i].y){ // 保证j在i前面
node tmp = v[j];
int cnt = C(now.x-tmp.x+now.y-tmp.y,now.y-tmp.y)%mod;
dp[i] = (dp[i] - cnt*dp[j]%mod+mod)%mod;
}
}
}
cout<<dp[v.size()-1]<<endl;
}
上课了,待更
…