• 序列上问题(逆元+整数快速幂)


    题目链接:https://ac.nowcoder.com/acm/contest/393/D

    链接:https://ac.nowcoder.com/acm/contest/393/D
    来源:牛客网

    题目描述

    请你求出一个 1 ~ N 的排列,使得它正好有 K 个逆序对。

    由于存在很多种这样的排列,所以要求出字典序最大的排列。

    因为排列可能很长,所以你只用输出类似于将这个排列放到 N+1 进制下的值,即 Ni=1p[i](N+1)imod 1000000007(109+7)∑i=1Np[i]∗(N+1)imod 1000000007(109+7) ,其中 p 是你求出的排列。

    输入描述:

    一行两个整数 N,K 。

    输出描述:

    一行一个整数,表示答案。
    示例1

    输入

    复制
    4 2

    输出

    复制
    2790

    说明

    p[1]=3,p[2]=1,p[3]=2,p[4]=4为字典序最大的结果。

    备注:

    对于所有100%100%的数据,有1N109,0KN×(N1)2

    思路:当时做这题的时候超时了,走了一遍N 还以为可以过的,但是N是10^9 不出意外应该是过不了的,交一发只是试试运气而已
    然后超时了就不知道怎么写了。。。
    结束比赛之后,看了官网的思路,并没有看别人题解,才发现原来题目中的公式是一个等差数列乘以一个等比数列!!! 这就好办了 ,高中学过呀 等差乘等比怎么求和 有公式的 自己推一下就好了
    知道了这个自己还是没有那么快做出来,还用到了整数快速幂 逆元的知识 就这两点了!! 都是自己懒,逆元现在才学,其实早就要学了,因为经常用到,自己实在太懒了,欠下的总是要还的
    这不还是要学,不学根本A不了这道题的,然后又跑去把逆元学了 ,终于给过了这道题,就是代码有点小复杂 具体看代码:
    #include<iostream>
    #include<string>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cstdio>
    using namespace std;
    const int maxn=1000+50;
    typedef long long LL;
    const LL mod=1e9+7;
    /**
    有以下几种情况
    当K<N时  只要把 刚好得到K个的数的M放在第一 其他的数从小到大是一个连续的序列 用数列求和就可以求出来了
    所以就有1~M-1  M  M+1~N   三段
    当K>N时  这时分为以下几段
    首先找到一个数M  依次从N排到M得到的逆序数  使得K减去这些数  K就小于M  所以再加上另外一个单独的数Z  其他数就是一个连续的序列 求和就行了
    N~M  Z 1~Z-1    Z+1`M-1  四段
    有这些知识就可以求解了
    */
    LL N,K;
    LL Quick_pow(LL n)//我们要求的是 (N+1)^n
    {
        LL D=N+1;
        LL cnt=D,ans=1;
        while(n)
        {
            if(n&1) ans=(ans*cnt)%mod;
            cnt=(cnt*cnt)%mod;
            n=n/2;
        }
        return ans;
    }
    LL Quick_pow1(LL n,LL m)//
    {
    
        LL cnt=n,ans=1;
        while(m)
        {
            if(m&1) ans=(ans*cnt)%mod;
            cnt=(cnt*cnt)%mod;
            m=m/2;
        }
        return ans;
    }
    LL solve(LL l,LL r,LL ql,LL qr)//代表左右区间 左边界的次方  右边界的次方
    {
    //    cout<<l<<" "<<r<<" "<<ql<<" "<<qr<<endl;
    //    cout<<"qr-ql+1 "<<qr-ql+1<<endl;
    //    LL z=Quick_pow(2);
    //    cout<<z<<endl;
        LL sum=0;
        sum+=l*Quick_pow(ql)%mod;
    //    cout<<"*sum"<<sum<<endl;
    //    cout<<"Quick_pow(ql+1)"<<Quick_pow(ql+1)<<endl;
    //    cout<<"Quick_pow(qr-ql+1)-1"<<Quick_pow(qr-ql+1)<<endl;
        //sum=(sum+((r-l)>=0?1:-1)*(Quick_pow(ql+1))*(Quick_pow(qr-(ql+1)+1)-1)/N-r*Quick_pow(qr+1)%mod)%mod;
        sum=(sum+((r-l)>=0?1:-1)*((Quick_pow(ql+1))*(Quick_pow(qr-(ql+1)+1)-1)%mod)*Quick_pow1(N,mod-2)%mod-r*Quick_pow(qr+1)%mod)%mod;
        //cout<<"**sum"<<sum<<endl;
        sum=-sum*Quick_pow1(N,mod-2)%mod;
        //cout<<"sum:"<<sum<<endl;
        if(sum<0) sum+=mod;
        return sum;
    }
    LL judge(LL mid)
    {
        LL z=K-(N-mid+1)*(mid+N-2)/2;
    
        if(z>=0&&z<mid-1) return 1;
        else if(z<0) return 2;//证明mid太小了
        else return 0;//证明mid太大了
    }
    int main()
    {
    //    cout<<20+75+125+1250<<endl;
        while(cin>>N>>K){
    
    
    //    scanf("%lld%lld",&N,&K);
        LL ans=0;
        if(K<N)
        {
            LL M=K+1;
            LL D=N+1;
            ans=(ans+M*D%mod);
            if(M>1)
            ans=(ans+solve(1,M-1,2,M))%mod;
            if(M<N)
            ans=(ans+solve(M+1,N,M+1,N))%mod;
            cout<<ans<<endl;//出错了!下次改
        }
        else
        {
            //找到数M
            LL l=1,r=N;
            LL M;
            while(l<=r)
            {
                LL mid=(l+r)>>1;
                LL flag=judge(mid);
                if(flag==1)
                {
                    M=mid;
                    break;
                }
                else if(flag==2)
                {
                    l=mid+1;
    
                }
                else r=mid-1;
            }
    //        cout<<"M:"<<M<<endl;
            //找到了M
            LL Z=K-(N-M+1)*(M+N-2)/2+1;//找到 了Z
            ans=(ans+solve(N,M,1,N-M+1))%mod;//N~M 连续下降的序列
    //        cout<<"*ans"<<ans<<endl;
            ans=(ans+Z*Quick_pow(N-M+2)%mod)%mod;//单独的Z
            if(Z!=1)
            {
                if(1<=Z-1)
                ans=(ans+solve(1,Z-1,N-M+3,N-M+3+(Z-2)))%mod;//1~Z-1  连续递增的数列
                if(Z+1<=M-1)
                ans=(ans+solve(Z+1,M-1,N-M+3+(Z-2)+1,N))%mod;//Z+1 ~ M-1连续递增序列
    
            }
            else
            {
                if(Z+1<=M-1)
                ans=(ans+solve(Z+1,M-1,N-M+3+(Z-2)+1,N))%mod;
            }
    
            cout<<ans<<endl;
        }
        }
        return 0;
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    Qt 学习之路 :自定义只读模型
    Qt 学习之路:QSortFilterProxyModel
    Qt 学习之路 :可视化显示数据库数据
    Qt 学习之路 :访问网络(4)
    Qt 学习之路:QFileSystemModel
    高级Bash脚本编程指南
    CGI
    shell学习
    【shell】while read line 与for循环的区别
    管道技巧-while read line
  • 原文地址:https://www.cnblogs.com/caijiaming/p/10567418.html
Copyright © 2020-2023  润新知