1 条题解

  • 0
    @ 2025-8-24 21:47:47

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar SoyTony
    不要畏惧停滞

    搬运于2025-08-24 21:47:47,当前版本为作者最后更新于2022-01-18 14:40:38,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目传送门

    前排广告

    省流

    $$\operatorname{ans}=\dfrac{(k+1)^{n-1}(k+1-n)}{k^n} $$

    说说我自己这个式子的理解吧。

    首先,如果人数多于座位数,即 n>kn>k,是绝对无法达成“都有座位”的,直接特判输出。

    接着,一个事件的概率=符合条件的情况数/总情况数,那么这个总情况数是非常好考虑的,nn 个人等概率的从 [1,k][1,k] 中选取一个,情况数自然是 knk^n

    然后来看符合条件的情况数,注意到题目中提到“如果当前位置已经有人则向下一个位置以此类推”,而我们并不确定开始选择的位置究竟是什么,不如添加一个 k+1k+1 号椅子并连成一个环,排除其他条件不谈,这 (k+1)(k+1) 个椅子的情况数为 (k+1)n(k+1)^n,然而是存在重复情况的。例如 1,3,2,4{1,3,2,4}2,4,1,3{2,4,1,3} 分别首尾相连成环以后,都是形如 1,3,2,4,(1){1,3,2,4,(1}) 的,也就是说这两种情况是等价的。而成环的连接点有 (k+1)(k+1) 个,去重后就有 (k+1)n1(k+1)^{n-1} 种情况。

    接下来是个人认为最难的地方了,这个 (k+1n)(k+1-n) 是怎么得来的?为什么要连成一个环?

    因为这里考虑的是符合条件“都有座位”的情况数,那么就要保证成功,方法是尽可能让每个人向右找座位的容错更高,但显然这个开始点是不确定的,我们连成一个环后,对于每个情况单独考虑,所有没有坐人的地方都可以删去并断开成正常的序列。因此选 nn 个座位后剩下的是 k+1nk+1-n 个座位,也就是可以断成 k+1nk+1-n 种不同的序列。

    对上述解释一句话概括:连成环后忽略编号,哪里为空断开哪里,哪里就是 k+1k+1 把椅子。

    然而需要一个高精度,因为式子中 (k+1)n1(k+1)^{n-1}knk^n 是无法化简,只要看 n+1kn+1-kknk^n 就可以了,这里会发现,前一个数字是个低精度,而高精取模低精后是低精度,所以麻烦的高精 gcd\gcd 其实可以拆分成一次高精取模低精和低精 gcd\gcd 就可以了。

    其他的看代码就能理解了。

    int t;
    struct largenum{
        int num[10005];
        inline void scan(){
            char s[10005];
            scanf("%s",s+1);
            int len=strlen(s+1);
            for(int i=len;i>=1;i-=4){
                int j=max(i-3,1);
                int sum=0;
                while(j<=i){
                    sum=sum*10+(s[j++]-'0');
                }
                num[++num[0]]=sum;
            }
        }
        inline void print(){
            printf("%d",num[num[0]]);
            for(int i=num[0]-1;i>=1;i--){
                printf("%04d",num[i]);
            }
            printf(" ");
        }
        largenum operator *(const largenum &tmp)const{
            largenum res;
            memset(res.num,0,sizeof(res.num));
            res.num[0]=num[0]+tmp.num[0];
            for(int i=1;i<=num[0];i++){
                for(int j=1;j<=tmp.num[0];j++){
                    res.num[i+j-1]+=num[i]*tmp.num[j];
                    res.num[i+j]+=res.num[i+j-1]/base;
                    res.num[i+j-1]%=base;
                }
            }
            while(res.num[res.num[0]]) res.num[0]++;
            while(!res.num[res.num[0]]&&res.num[0]>1) res.num[0]--;
            return res;
        }
        largenum operator *(const int &tmp)const{
            largenum res;
            memset(res.num,0,sizeof(res.num));
            res.num[0]=num[0];
            int laz=0;
            for(int i=1;i<=res.num[0];i++){
                res.num[i]=num[i]*tmp+laz;
                laz=res.num[i]/base;
                res.num[i]=res.num[i]%base;
            }
            while(laz){
                res.num[++res.num[0]]=laz%base;
                laz/=base;
            }
            while(res.num[res.num[0]]) res.num[0]++;
            while(!res.num[res.num[0]]&&res.num[0]>1) res.num[0]--;
            return res;
        }
        int operator %(const int &tmp)const{
            int res=0;
            for(int i=num[0];i>=1;i--){
                res=(res*base+num[i])%tmp;
            }
            return res;
        }
        largenum operator /(const int &tmp)const{
            largenum res;
            memset(res.num,0,sizeof(res.num));
            res.num[0]=num[0];
            ll laz=0;
            for(int i=res.num[0];i>=1;i--){
                res.num[i]=(laz*base+num[i])/tmp;
                laz=(laz*base+num[i])%tmp;
            }
            while(!res.num[res.num[0]]&&res.num[0]>1) res.num[0]--;
            return res;
        }
    }ans1,ans2,tmp;
    inline largenum q_pow(largenum x,int p){
        largenum res;
        memset(res.num,0,sizeof(res.num));
        res.num[0]=res.num[1]=1;
        while(p){
            if(p&1){
                res=res*x;
            }
            x=x*x;
            p>>=1;
        }
        return res;
    }
    inline int gcd(int a,int b){
        if(!b) return a;
        return gcd(b,a%b);
    }
    int n,k,m;
    int main(){
        t=read();
        while(t--){
            n=read(),k=read();
            m=k+1-n;
            if(n>k){
                printf("0 1\n");
                continue;
            }
            memset(ans1.num,0,sizeof(ans1.num));
            memset(ans2.num,0,sizeof(ans2.num));
            ans1.num[0]=ans2.num[0]=1;
            ans1.num[1]=k+1,ans2.num[1]=k;
            ans1=q_pow(ans1,n-1);
            ans2=q_pow(ans2,n);
            ans1=ans1*m;
            //ans1.print();
            //ans2.print();
            tmp=ans2;
            int g=tmp%m;
            //printf("\n%d\n",g);
            g=gcd(m,g);
            //printf("%d\n",g);
            ans1=ans1/g;
            //printf("%d\n",m);
            ans1.print();
            ans2=ans2/g;
            ans2.print();
            printf("\n");
        }
        return 0;
    }
    
    
    • 1

    信息

    ID
    2403
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者