洛谷 「CF1557C Moamen and XOR」

传送门

说句闲话,赛时因为和 @Leasier 聊天导致没写完代码。

\(\texttt{Description}\)

  • 给定 \(n\)\(k\)\(n\) 为序列长度,\(k\) 表示序列中的值上限为 \(2^k-1\)

  • 求出有多少种满足 \(a_1\ \mathrm{and}\ a_2\ \mathrm{and}\ a_3\ \mathrm{and}\ \dots\ \mathrm{and}\ a_n\ge a_1\ \mathrm{xor}\ a_2\ \mathrm{xor}\ a_3\ \mathrm{xor}\ \dots\ \mathrm{xor}\ a_n\) 的序列 \(a\),答案对 \(10^9+7\) 取模。

  • 多测。

数据范围:\(1\le n\le2\times10^5,0\le k\le2\times10^5\)

\(\texttt{Solution}\)

  • 不难看出 \(k\) 为每个数的二进制的最大位数。

  • 对于二进制的每一位分开考虑。

  • 令二进制数最高位为第一位,次高位为第二位,以此类推。

  • \(p1\)\(a_1\ \mathrm{and}\ a_2\ \mathrm{and}\ a_3\ \mathrm{and}\ \dots\ \mathrm{and}\ a_n\)\(p2\)\(a_1\ \mathrm{xor}\ a_2\ \mathrm{xor}\ a_3\ \mathrm{xor}\ \dots\ \mathrm{xor}\ a_n\)

  • 要满足 \(p1\ge p2\) 的条件,分两种情况讨论:

    • \(p1=p2\),那么 \(p1\)\(p2\) 的每一位一定都相等,对于每一位:

      • 若为 \(0\):则 \(a_1\)\(a_n\) 的这一位必然至少有一个为 \(0\),且为 \(1\) 的个数有偶数个(因为偶数个 \(1\) 异或结果为 \(0\)),所以方案数为 \(\sum\mathcal{C}_{n}^{i}\)\(i\equiv0\pmod2\)\(i\ne n\)

      • 若为 \(1\):则 \(a_1\)\(a_n\) 的这一位必然全为 \(1\),但如果 \(n\) 为偶数,则这样的情况不成立。当这样的情况成立时,方案数为 \(1\)

    • \(p1>p2\),那么一定存在某一位 \(k\) 使得 \(p1\) 的第 \(k\) 位为 \(1\)\(p2\) 的第 \(k\) 位为 \(0\),并且满足 \(p1\)\(p2\)\(1\)\(k-1\) 位都相等,但是只有 \(k\) 为偶数时这种情况才能成立

    • 于是我们可以想到枚举这个 \(k\)。对于每个 \(k\),方案数为前面 \(k-1\) 位都相等的方案数(已在上面讨论过)乘上 \((2^n)^{n-k}\),因为第 \(k\) 位后面的可以随便乱选。

  • 那么这道题就分析完了,之后套一下柿子就可以了。

时间复杂度:\(\mathcal{O}(n\log n)\)

\(\texttt{Code}\)

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;

ll fac[200005];
inline void init(ll n) {
	fac[0] = 1;
	for (ll i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i % mod;
}
inline ll quick_pow(ll a, ll k, ll p) {
	ll res = 1;
	a %= p;
	while (k) {
		if (k & 1) res = res * a % p;
		a = a * a % p;
		k >>= 1;
	}
	return res;
}
inline ll inv(ll x, ll p) {return quick_pow(x, p - 2, p);}
inline ll getC(ll n, ll m, ll p) {return fac[n] * inv(fac[n - m] * fac[m] % p, p) % p;}

int main() {
	init(2e5);
	int T;
	scanf("%d", &T);
	while (T--) {
		ll n, k, ans = 0, sum = 0;
		scanf("%lld %lld", &n, &k);
		for (ll i = 0; i < n; i += 2) sum = (sum + getC(n, i, mod)) % mod;
		if (n & 1) ans = (ans + quick_pow(sum + 1, k, mod)) % mod;
		else {
			for (ll i = 0; i < k; i++) ans = (ans + quick_pow(sum, i, mod) * quick_pow(quick_pow(2, n, mod), k - i - 1, mod) % mod) % mod;
			ans = (ans + quick_pow(sum, k, mod)) % mod;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2021-08-12 14:22  Sham_Devour  阅读(82)  评论(0)    收藏  举报