LOJ #6432. 「PKUSC2018」真实排名

题目在这里......

对于这道题,现场我崩了......谁跟我说组合数O(n)的求是最快的?

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;
const ll mod = 998244353;
const int N = 1e5 + 10;

ll ksm(ll x, ll n) {
    ll ans = 1ll; //printf("%lld ", x);
    while( n) {
        if( n&1) ans = ans * x % mod; x = x * x % mod;
        n >>= 1;
    }
//    printf("ans = %lld, %lld\n", ans, x);
    return ans;
}
ll zt[N];
ll lucas(int m, int n) {
    ll x = zt[m] * zt[n-m] % mod; //printf("%lld\n", ksm(x, mod-2));
    return zt[n] * ksm(x, mod-2) % mod;
}
int data[N], a[N], n, k;
ll query1(int x) {
    int r = lower_bound(data+1, data+1+n, a[x]) - data;
    int l = upper_bound(data+1, data+1+n, (a[x] + 1 >> 1) - 1) - data - 1;
    if( l + n - r < k) return 0ll;
    return lucas(k, l + n - r);
}

ll query2(int x) {
    int l = lower_bound(data+1, data+1+n, a[x]) - data;
    int r = lower_bound(data+1, data+1+n, a[x] << 1) - data;
    if( l == r) r ++;
    if( r - l > k) return 0ll;
    return lucas(k-r+l, l + n - r);
}

int main() {
    zt[1] = zt[0] = 1ll;
    for( int i = 2; i <= N-2; i ++) zt[i] = zt[i-1] * i % mod;
//    for( int i = 1; i <= 10; i ++) printf("%d ", zt[i]); puts("");
    scanf("%d%d", &n, &k);
    for ( int i = 1; i <= n; i ++) scanf("%d", a+i), data[i] = a[i];
    sort(data+1, data+1+n);
    for ( int i = 1; i <= n; i ++) 
        printf("%lld\n", (query1(i) + query2(i)) % mod);
}
View Code

我们先复制一份排个序

我们考虑2种情况:

  1:当前节点不变的情况,则大于他的节点和小于他的一半的节点都可以改变(显然),注意一下0,然后我们可以求得可以改变的数的个数t1。

  2:当前节点要改变,那么想要维持他的排名不变,那么大于等于他且小于他的2倍的数都要改变(显然),然后我们可以求得可以改变的数t2。

然后就是求组合数(心态炸了QAQ)......

预处理一下,然后因为mod为质数,所以可以用费马小定理在log的时间内求得逆元......

每次logN的求t1, t2,。logmod的时间求逆元,所以复杂度是O(nlogn+logmod)的。

费马小定理 : a^(p-1) % p == 1 (p为质数);

组合数 : 

(当时我用这个公式每次O(n)的求组合数只有45分QAQ,应该预处理阶乘)


猜你喜欢

转载自www.cnblogs.com/miecoku/p/9171932.html