P11870 [威海市赛2024] 找数
P11870 [威海市赛2024] 找数
题目描述
喂喂喂?在不在?小威给你下发了一个任务:
给定 \(n\) 和 \(m\),从 \(1 \sim n\) 中随意挑出 \(m\) 个数,之后对选出的数排序,称为 \(p\)。
小威想知道,排序后的序列 \(p\) 中,奇数位全为奇数,偶数位全为偶数的方案数,对 \(998\ 244\ 353\) 取模。
很不巧,你在他问"在不在?"的时候就回复他了。这下不得不帮他解决问题了 😦
输入格式
一行两个整数 \(n, m\),含义如上所述。
对于所有数据,满足:\(1 \leq m \leq n \leq 10^6\)。
输出格式
输出一个整数,表示合法方案数对 \(998\ 244\ 353\) 取模后的结果。
输入输出样例 #1
输入 #1
2 1
输出 #1
1
输入输出样例 #2
输入 #2
4 2
输出 #2
3
输入输出样例 #3
输入 #3
8 5
输出 #3
6
题解:
前置知识:组合数学,快速幂,线性求逆元(不必要)
题意
在一个排列 \(n\) 中,找出长为 \(m\) 的上升子序列 \(p\),使得 \(p\) 内的每个数与下标奇偶性相同,即 \(p_i\bmod 2=i\bmod 2\)。
思路
一开始拿到这个题以为是个容斥,选出所有的奇偶性满足条件的再减去有逆序对的,但是感觉不合法序列不算很简单,所以只写了前面的
(我没写不合法,等待大佬)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int mod=998244353;
int c(int a,int b){
int jca=1,jcb=1,jcab=1;
for(int i=2;i<=a;i++){
if(i<=b)jcb=(jcb*i)%mod;
if(i<=(a-b))jcab=(jcab*i)%mod;
jca=(jca*i)%mod;
}
return jca/jcb/jcab;
}
int ans;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
ans=c((n+1)>>1,(m+1)>>1)*c((n+1)>>1,m>>1)%mod
cout<<ans;
return 0;
}
又神游想了两分钟,感觉可以转化:
正解:
对于 \(p\) 中每个数,我们把它加上他的下标,成为 \(p_i+i,i\in[1,m]\),最大的数就是当 \(p_n=n,i=m\) 时即为 \(n+m\),同理最小值是 \(1+1=2\),此时值域为 \([2,n+m]\),由于序列值和下标奇偶性相同,所以其和一定为偶数,因此我们只需要找区间 \([2,n+m]\) 中的偶数就好了。
易得,区间 \([2,n+m]\) 中偶数的个数为 \(\lfloor\frac{n+m}{2}\rfloor\)。然后问题就转化成了从 \(\lfloor\frac{n+m}{2}\rfloor\) 个偶数里面选择 \(m\) 个偶数,答案就非常简单了,是 \(C_{\lfloor\frac{n+m}{2}\rfloor}^m\)。由于只有一次询问,所以这里可以不写线性求逆。
如果你还是看不懂,我们考虑这样一组数据:
5 3
此时原序列为:1 2 3 4 5
比方说我们选取 \(1、2、5\),此时进行上述操作,变成:\(1+1,2+2,5+3=2,4,8\)。同样的,当选择 \(1 4 5\) 或者 \(3 4 5\) 时,操作后也都在 \(\{2,4,6,8\}\) 这四个数字之间。
也就是说,我们的问题就是从 \(\{2,4,6,8\}\) 中取出三个偶数。结论如上文。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long//禁忌大法
int n,m;
const int mod=998244353;
int qpow(int a,int b){//快速幂,后面逆元用
int cnt=1;
while(b){
if(b&1)cnt=cnt*a%mod;
a=a*a%mod;
b>>=1;
}
return cnt%mod;
}
int c(int a,int b){
int jca=1,jcb=1,jcab=1;
//a的阶乘、b的阶乘、(a-b)的阶乘
for(int i=2;i<=a;i++){
if(i<=b)jcb=(jcb*i)%mod;
if(i<=(a-b))jcab=(jcab*i)%mod;
jca=(jca*i)%mod;
}
return (jca*qpow(jcb,mod-2))%mod*qpow(jcab,mod-2)%mod;//组合数,除法的时候要逆元
}
int ans;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
ans=c((n+m)>>1,m);
cout<<ans;
return 0;//好习惯
}
//by lain_yc

浙公网安备 33010602011771号