洛谷 P5368 [PKUSC2018] 真实排名(组合数学)

题目传送门

解题思路

对于每一个数 a_i,我们可以分情况讨论:

情况一:a_i 不翻倍

若 a_i 不翻倍,我们可以发现 \frac {a_i} {2} 到 a_i-1 都是不能翻倍的,否则就会改变 a_i 的排名。

设这个范围内有 x 个数,则有 C_{n-x}^{k} 种方案。

情况二:a_i 翻倍

若 a_i 翻倍,我们同样可以发现 a_i 到 2 \times a_i 都是要跟着翻倍的,否则也会改变 a_i 的排名。

设这个范围内的数有 x 个,则有 C_{n-x}^{k-x} 种方案。

我们可以用二分找出 \frac {a_i} {2} 和 2 \times a_i 的位置,算出对应的 x

特殊情况:

注意,如果 a_i 为 0,则方案数应该为 C_n^k

如果有重复的 a_i,要以第一个 a_i 的位置算。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int mod=998244353;
struct node{
	int num,id;
}a[100001];
int f[100001],g[100001];
int ans[100001];
map<int,int> fir;
bool cmp(node x,node y)
{
	return x.num<y.num;
}
int c(int n,int m)
{
	if(m>n)return 0;
	return f[n]*g[m]%mod*g[n-m]%mod;
}
int qpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
		{
			res*=a;
			res%=mod;
		}
		b>>=1;
		a*=a;
		a%=mod;
	}
	return res;
}
int erfen(int x)
{
	int l=1,r=n;
	int mid,ans=0;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(a[mid].num<=x)
		{
			ans=mid;
			l=mid+1;
		}
		else
			r=mid-1;
	}
	return ans;
}
main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	cin>>n>>k;
	f[0]=g[0]=1;
	for(int i=1;i<=n;i++)
	{
		f[i]=f[i-1]*i%mod;
		g[i]=g[i-1]*qpow(i,mod-2)%mod;
		f[i]=(f[i]+mod)%mod;
		g[i]=(g[i]+mod)%mod;
	}
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].num;
		a[i].id=i;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		if(fir.count(a[i].num)==0)
		{
			fir[a[i].num]=i;
		}
	}
	int x,wz;
	for(int i=1;i<=n;i++)
	{
		if(a[i].num==0)
		{
			ans[a[i].id]=(c(n,k)+mod)%mod;
			continue;
		}
		if(a[i].num%2==0)
			wz=erfen(a[i].num/2-1);
		else
			wz=erfen(a[i].num/2);
		x=n-(fir[a[i].num]-wz);
		ans[a[i].id]+=(c(x,k)+mod)%mod;
		wz=erfen(a[i].num*2-1);
		x=wz-fir[a[i].num]+1;
		if(k>=x)
		{
			ans[a[i].id]+=(c(n-x,k-x)+mod)%mod;
			ans[a[i].id]=(ans[a[i].id]+mod)%mod;
		}
	}
	for(int i=1;i<=n;i++)
	{
		cout<<(ans[i]+mod)%mod<<endl;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值