题目链接
https://codeforces.com/problemset/problem/1436/C
ps:不知道为什么vj上wa,cf上ac???
解题思路
模拟二分的过程,如果要找的pos位置大于等于mid,说明l取小了,根据题目代码, l=mid+1;
;如果要找的pos位置小于mid,说明r取大,根据题目代码,r=mid;
。
不同的是,我们要统计二分过程中所有mid位置上比x大的数有多少,比x小的数有多少。
为什么?
我们在乎的只是与二分有关的点,而与二分有关的点只是mid点,其他的位置的值爱是多少是多少,是多少都不影响我们二分,因为二分过渡到下一个过程的判断条件只是比较mid位置值与x的大小关系。
因此,我们假设找出mid位置上比x小的个数为le(本意为less,但是具有二义性),假设从所有比x小的个数为sumle,则我们要从sumle中选出le个数排列在比x小的mid的位置上,即A(le,sumle);
类似的,我们假设找出mid位置上比x大的个数为gr(本意为greater),假设从所有比x大的个数为sumgr,则我们要从sumgr中选出gr个数排列在比x大的mid的位置上,即A(gr,sumgr);
同时,对于吧剩下的n-sumgr-sumle个数,这其中还含有x,我们要把它排除,即还剩n-sumgr-sumle-1个数,我们要将这些数放在剩下的位置上,随便放就行,即A(n-sumgr-sumle-1,n-sumgr-sumle-1),即(n-sumgr-sumle-1)!。
这三个排列数相乘就是答案了。
AC代码
#include<bits/stdc++.h> #define ll long long using namespace std; const ll mod=1e9+7; const int N=1e4+1000;//开大点,我怕了 int n,x,pos,le,gr; ll fac[N]; void getfac()//求阶乘 { fac[0]=1; for(int i=1;i<=10100;i++) fac[i]=fac[i-1]*i%mod; } ll KSM(ll x,ll p)//快速幂//不会的话代码下面有知识点补充的链接 { ll sum=1; while(p>0) { if(p&1LL) sum=sum*x%mod; x=x*x%mod; p>>=1; } return sum; } ll A(int x,int y)//排列数//用了个逆元//不会逆元什么的话,代码下面有知识点补充的链接 { return fac[y]*KSM(fac[y-x],mod-2)%mod; } int main() { getfac(); cin>>n>>x>>pos; int l=0,r=n; while(l<r) { int mid=l+r>>1; if(mid<=pos) { if(mid!=pos) le++;//当mid与pos位置相等的时候,说明查找到的是输入的那个,不能算比x大或者比x的小的,所以le不变,只有mid!=pos时才++ l=mid+1;//仿照题目代码写 } else { gr++; r=mid;//仿照题目代码写 } } cout<<A(le,x-1)*A(gr,n-x)%mod*A(n-gr-le-1,n-gr-le-1)%mod<<endl;//排列数嘛 return 0; }
知识点的补充
总结
说实话,我真没觉得自己能AC,因为二分的边界问题太可怕了,细节太多,没想到混过去了。