【蓝桥杯备赛】——每日一题(智力测试)(离散化+组合数+逆元+前缀积)(第十五届蓝桥杯C++组大赛)(day4)

前言

 今天是蓝桥杯备赛每日一题的第四天,感兴趣的小伙伴,可以订阅一下专栏。感兴趣的友友可以一起跟着做一下!!!今天的题目特别繁琐,博主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,欢迎在评论区交流,笔者看到会及时回复

请大家一定一定要关注!!!
请大家一定一定要关注!!!
请大家一定一定要关注!!!
友友们,你们的支持是我持续更新的动力~

创作不易,求关注,点赞,收藏,谢谢~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wxchyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值