Loj 6432. 「PKUSC2018」真实排名 (组合数)

题面

Loj

题解

枚举每一个点

分两种情况

翻倍or不翻倍


\(1.\)如果这个点\(i\)翻倍, 要保持排名不变,哪些必须翻倍,哪些可以翻倍?

必须翻倍: \(a[i] \leq a[x] < a[i]*2\)

那么其他的都可以选择性翻倍


\(2.\) 考虑点\(i\)不翻倍,

不能翻倍的: \(a[i]/2 \leq a[x] < a[i]\)

注意有和\(a[i]\)相等的可以翻倍


以上可以排序后,二分+组合数算

细节比较多,具体看代码

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

#define ll long long

using namespace std;

const int N = 100010;
const ll Mod = 998244353;
ll ksm(ll x, ll y) {
	ll s = 1;
	while (y) {
		if (y & 1) s = s * x % Mod;
		y >>= 1;
		x = x*x%Mod;
	}
	return s;
}

ll fac[N];

ll C(int n, int m) {
	if (n < m || n < 0 || m < 0) return 0;
	return fac[n]*ksm(fac[m], Mod-2)%Mod*ksm(fac[n-m], Mod-2)%Mod;
}

int a[N], b[N];

template<class T> inline void read(T &x) {
	x = 0;  char c = getchar(); bool f = 0;
	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
	x = f ? -x : x;
	return ;
}

int main() {

	int n, k;
	read(n); read(k);
	for (int i = 1; i <= n; i++) read(a[i]), b[i] = a[i];
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i-1]*i%Mod;
	
	sort(b+1, b+1+n);
	
	for (int i = 1; i <= n; i++) {

		if (!a[i]) {
			printf("%lld\n", C(n, k));
			continue;
		}
		
		ll ans1 = 0, ans2 = 0;
		
		int x1 = lower_bound(b+1, b+1+n, (a[i]+1)/2) - b - 1;
		int x2 = lower_bound(b+1, b+1+n, a[i]) - b;
		ans1 = C(x1+n-x2, k);

		int x3 = lower_bound(b+1, b+1+n, a[i]*2) - b;
		
		ans2 = C(n - x3 + x2, k - x3 + x2);

		printf("%lld\n", (ans1 + ans2) % Mod);		
	}	
	return 0;
}


posted @ 2019-01-18 17:10  zzy2005  阅读(132)  评论(0编辑  收藏  举报