对于这道题,现场我崩了......谁跟我说组合数O(n)的求是最快的?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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); }
我们先复制一份排个序
我们考虑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,应该预处理阶乘)