前言
今天是蓝桥杯备赛每日一题的第四天,感兴趣的小伙伴,可以订阅一下专栏。感兴趣的友友可以一起跟着做一下!!!今天的题目特别繁琐,博主debug了好一会,会把遇到的坑写下来,希望友友们能够快速AC。
一、题目描述
输入格式
输出格式
示例
示例输入
4 4 2
4 2 3 1
2 1 2 1
4 4 1 1
2 2 2 4
示例输出
4
0
样例解释
数据范围
题目大意
有一个n * m的棋盘,每个格子都有( R r , C c ) R_{r},C_{c}) Rr,Cc)的权重,T次询问,问每次从一个格子到另一个格子有几种走法,格子能移动的条件为 R r ′ > R r R_{r'} > R_{r} Rr′>Rr&&不存在 R r ′ > R r ′ ′ > R r R_{r'} > R_{r''} > R_{r} Rr′>Rr′′>Rr 或者 C c ′ > C c C_{c'} > C_{c} Cc′>Cc && 不存在 C c ′ > C c ′ ′ > C c C_{c'} > C_{c''} > C_{c} Cc′>Cc′′>Cc ,答案对1000000007取模
题目思路
思路:
1 由于我们需要计算方案数,我们肯定需要选择能走的途径,在能走的路径种进行选择。
2 假设起点到终点的横坐标差我们记为dx,纵坐标差我们记为dy(离散化过后的)
3 dx+dy就是我们到终点所要走的步数,在这其中我们需要选择dx次,所以方法有
C
d
x
+
d
y
d
x
C_{dx+dy}^{dx}
Cdx+dydx种
4 由于mod是质数,求组合数我们使用费马引理求逆元,再通过组合数公式求得
C
d
x
+
d
y
d
x
C_{dx+dy}^{dx}
Cdx+dydx
5 由于权值还可能出现相同的情况,所以我们需要乘以重复权值的数量。
6 如何求呢?由于我们需要m次进行询问,所以我们需要预处理,可以用类似前缀和的思想,我们利用前缀积
7 最后由于权值的范围到了10e8,数组肯定存不下,我们需要利用离散化。
8 最后答案等于组合数乘以横纵区间的重复前缀积
我们总结一下流程:
1 离散化横纵坐标权值
2 求组合数通过乘法逆元求解,预处理出fact[2*N]和infact[N]数组
3 前缀积求横纵重复权值
4 答案输出前缀积乘以组合数的值
注意:
1 笔者二分的时候应该是<= 搞成>=
2 find的时候应该是num_R传进去,直接传成坐标了
3 要开long long不然相乘会爆精度
4 n,m不同,求前缀积计数要分别统计重复数
5 首先需要特判,特判的时候要除掉起点等于终点,因为这也算一种方案
6 求阶乘逆元时,要开fact[2 * N],因为求组合数是 fact[a + b] * infact[a] * infact[b]
我们看下代码:
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5+10,mod = 1e9+7;
int n,m,T;
int sx,sy,ex,ey;
vector<int>v1,v2;//离散化
int fact[2*N],infact[N];//求组合数
int Cnt1[N],Cnt2[N];//记录权值出现的次数
int num_R[N],num_C[N];//存储权值
int Cnt_R[N],Cnt_C[N],inCnt_R[N],inCnt_C[N];//存储前缀积 和 它们的逆元
int qmi(int a,int k,int p){ //快速幂
ll res = 1;
while(k){
if(k&1) res = (ll)res * a %p;
a = (ll)a * a % p;
k>>=1;
}
return res;
}
int find1(int x){ //离散化后二分找到下标
int l = 0,r = v1.size()-1;
while(l<r){
int mid = (l + r) >> 1;
if(x <= v1[mid]) r = mid;
else l = mid + 1;
}
return r + 1;
}
int find2(int x){
int l = 0,r = v2.size()-1;
while(l<r){
int mid = (l + r) >> 1;
if(x <= v2[mid]) r = mid;
else l = mid + 1;
}
return r + 1;
}
int main(){
cin>>n>>m>>T;
for(int i = 1;i<=n;++i){
cin>>num_R[i];
v1.push_back(num_R[i]);
}
for(int j = 1;j<=m;++j){
cin>>num_C[j];
v2.push_back(num_C[j]);
}
//离散化
sort(v1.begin(),v1.end());sort(v2.begin(),v2.end());
v1.erase(unique(v1.begin(),v1.end()),v1.end());
v2.erase(unique(v2.begin(),v2.end()),v2.end());
//预处理组合数
fact[0] = infact[0] = 1;
for (int i = 1; i < 2 * N; i ++ )
{
fact[i] = (ll)fact[i - 1] * i % mod;
if(i < N) infact[i] = (ll)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
//预处理前缀积
for(int i = 1;i <= n;++i){
int t1 = find1(num_R[i]);
Cnt1[t1]++;
}
for(int i = 1;i <= m;++i){
int t1 = find2(num_C[i]);
Cnt2[t1]++;
}
Cnt_R[0] = Cnt_C[0] = 1;
for(int i = 1;i <= v1.size();++i){
Cnt_R[i] = (ll)Cnt_R[i-1] * Cnt1[i] % mod;
inCnt_R[i] = qmi(Cnt_R[i],mod-2,mod);
}
for(int i = 1;i <= v2.size();++i){
Cnt_C[i] = (ll)Cnt_C[i-1] * Cnt2[i] % mod;
inCnt_C[i] = qmi(Cnt_C[i],mod-2,mod);
}
while(T--){
cin>>sx>>sy>>ex>>ey;
if (num_R[sx] > num_R[ex] || num_C[sy] > num_C[ey] || (num_R[sx] == num_R[ex] && sx != ex) || (num_C[sy] == num_C[ey] && sy != ey)) { //当权值大于或等于的时候直接特判
cout<<"0"<<endl;
continue;
}
sx = find1(num_R[sx]),sy = find2(num_C[sy]),ex = find1(num_R[ex]),ey = find2(num_C[ey]);
int dx = ex - sx,dy = ey - sy;
int ans = (ll)fact[dx+dy] * infact[dx]%mod * infact[dy]%mod;
if(sx!=ex) ans = (ll)ans * Cnt_R[ex - 1] %mod * inCnt_R[sx] %mod;
if(sy!=ey) ans = (ll)ans * Cnt_C[ey - 1] %mod * inCnt_C[sy] %mod;
cout<<ans<<endl;
}
}
写在文末
这道题细节比较多,希望友友们耐心debug,欢迎在评论区交流,笔者看到会及时回复。
请大家一定一定要关注!!!
请大家一定一定要关注!!!
请大家一定一定要关注!!!
友友们,你们的支持是我持续更新的动力~