题目链接

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;
}

知识点的补充

快速幂
这里面的D题既有逆元的讲解,又有ksm的讲解

总结

说实话,我真没觉得自己能AC,因为二分的边界问题太可怕了,细节太多,没想到混过去了。