题目链接:https://ac.nowcoder.com/acm/contest/393/D
链接:https://ac.nowcoder.com/acm/contest/393/D
来源:牛客网
题目描述
请你求出一个 1 ~ N 的排列,使得它正好有 K 个逆序对。
由于存在很多种这样的排列,所以要求出字典序最大的排列。
因为排列可能很长,所以你只用输出类似于将这个排列放到 N+1 进制下的值,即 N∑i=1p[i]∗(N+1)imod 1000000007(109+7)∑i=1Np[i]∗(N+1)imod 1000000007(109+7) ,其中 p 是你求出的排列。
由于存在很多种这样的排列,所以要求出字典序最大的排列。
因为排列可能很长,所以你只用输出类似于将这个排列放到 N+1 进制下的值,即 N∑i=1p[i]∗(N+1)imod 1000000007(109+7)∑i=1Np[i]∗(N+1)imod 1000000007(109+7) ,其中 p 是你求出的排列。
输入描述:
一行两个整数 N,K 。
输出描述:
一行一个整数,表示答案。
备注:
对于所有100%100%的数据,有1≤N≤109,0≤K≤N×(N−1)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; }