题意大概为每人都有排名(比他高的人数+1)与能力 拿到书本后能力变为t倍,一共k本书,问每个人排名不变的方案数(思路见代码注释 很详细//xyd的ID貌似为4646
下面是代码(赛时思路(正解但是pts10)求调栓Q)
//逆元?已经不会写力
//exgcd(a,x)=exgcd(x,a%n)=1 (mod n )
//a=t1*n+p1
//a*x=1 (mod n)
//t1*n*x+p1*x=1
//消愁 赛时想手推exgcd 没推出来
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10,MOD=1e9+7;
int n,k,t;
int w[N];
int p[N];
int ans;
int jie[N];
struct s
{
int w;
int index;
}a[N],b[N];
int qpow(int a,int b)//MOD为质数 费马小定理求逆元
{
int res=1;
a=(a%MOD+MOD)%MOD;
while(b)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void solve1()
{
for(int i=1;i<=n;i++)
{
//no发给自己
ans=0;
int l=1,r=n;
while(l<=r)
{
int mid=l+r>>1;
if(b[mid].w>w[i]) l=mid+1;
else r=mid-1;//b_mid<=w[i]
}
int xx=l-2;//高于自己的 因为自己肯定比自己高所以-2
int all=p[i]-1+(n-xx-1);//C(all,k)
ans=(ans+jie[all]*qpow(jie[k],MOD-2)%MOD*qpow(jie[all-k],MOD-2)%MOD)%MOD;
//发给自己
int L=1,R=n;
while(L<=R)
{
int mid=L+R>>1;
if(a[mid].w>w[i]*t) L=mid+1;
else R=mid-1;
}
int X=L-1;//高于自己的
int del=(p[i]-1)-X;//被超越的
int al=n-del-1,Y=k-1-del;//C(al,y)
if(Y==0) ans=(ans+1)%MOD;
else if(k-1-del>0) ans=(ans+jie[al]*qpow(jie[Y],MOD-2)%MOD*qpow(jie[al-Y],MOD-2)%MOD )%MOD;
printf("%lld\n",ans%MOD);
}
}
bool cmp(s a,s b)
{
return a.w>b.w;
}
signed main()
{
scanf("%lld%lld%lld",&n,&k,&t);
jie[0]=1;
for(int i=1;i<=n;i++) jie[i]=(jie[i-1]*i)%MOD;
for(int i=1;i<=n;i++) scanf("%lld",&a[i].w),a[i].index=i,w[i]=a[i].w;
sort(a+1,a+1+n,cmp);
int lst=0;
for(int i=1;i<=n;i++)
{
b[i].index=a[i].index;
b[i].w=a[i].w*t;
p[a[i].index]=lst+1;
if(a[i].w!=a[lst].w) lst++;
}
if(k==n)
{
for(int i=1;i<=n;i++) puts("1");
exit(0);
}
solve1();
}
//不发给自己 发给排名比自己高的 or 乘t不超过自己的 组合
//发给自己 要保证发给被自己超越的人 剩下的取组合
//如何高效寻找排名 先排名 再建立×t的集合 互相找?
思路是对的(和正解一样),甚至方法都最优(?目前看到唯一且最优吧),但是实现上可能哪里出了问题 能过n为100的样例 但是拿10pts
玄关