A

注意到奇数和奇数的乘积仍是奇数。

偶数和偶数的乘积仍是偶数。

所以答案就是 \(n\) 减去奇偶连续段段个数。

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

void sovle() {
	int n; scanf("%d", &n);
	vector<int> a(n);
	int cnt = 0;
	for (int i = 0; i < n; i ++) scanf("%d", &a[i]);

	for (int i = 1; i < n; i ++) if (a[i - 1] % 2 == a[i] % 2) ++ cnt;
	printf("%d\n", cnt);
}

int main() {
	int T; scanf("%d", &T);
	while (T --) sovle();
	return 0;
}

B

注意到如果一个序列正序之后再倒序如果不考虑他们两个序列之间的逆序对个数,两个序列的逆序对总和是 \(\frac{n \times (n - 1)}{2}\)

再考虑他们两个序列之间的逆序对个数,不难发现也相同。

所以答案为 \(n! \times n \times (n-1)\)

注意两个 int 相乘要开 long long

之前一般全开 long long 所以不会有这个问题。

#include <cstdio>

typedef long long ll;
const ll MOD = 1e9 + 7;

void solve() {
	int n; scanf("%d", &n);
	ll cnt = 1, mul = 1ll * n * (n - 1) % MOD;
	for (int i = 2; i <= n; i ++) cnt = cnt * i % MOD;
	printf("%lld\n", cnt * mul % MOD);
}

int main() {
	int T; scanf("%d", &T);
	while (T --) solve();
	return 0;
}

C

不难发现如果 \([l, r]\) 是一个可行的选择区间,那么 \([l + 1, r - 1]\) 一定不可能是一个可能的选择区间。

所以我们就可以双指针统计答案即可。

我脑子抽了写了个二分,发现写麻烦了。

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

const int N = 1e5 + 10;
vector<int> factor[N];
int cnt[N], b[N], n, m, a[N], maxw;

bool check(int len) {
	// printf("check %d\n", len);

	for (int i = 1; i <= m; i ++) cnt[i] = 0;

	int elasped = m;
	for (int i = 1; i < len; i ++) if (b[i]) {
		for (int x : factor[i]) 
			if (x <= m) elasped -= (cnt[x] == 0), cnt[x] += b[i]; 		
	}  
	int l = 0;
	for (int r = len; r <= maxw; r ++) {
		if (b[r]) {

			for (int x : factor[r]) 
				if (x <= m) elasped -= (cnt[x] == 0), cnt[x] += b[r];
		}


		// printf("[%d, %d] elasped: %d\n", l + 1, r, elasped);
		// for (int i = 1; i <= m; i ++) printf("cnt[%d] = %d\n", i, cnt[i]);
		if (elasped == 0) return true;

		++ l;
		if (b[l]) {
			for (int x : factor[l]) 
				if (x <= m) cnt[x] -= b[l], elasped += (cnt[x] == 0);
		}
	}

	return false;
}

void solve() {
	maxw = 0;
	int minw = 1e5 + 10;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i ++) scanf("%d", &a[i]), maxw = max(maxw, a[i]), minw = min(minw, a[i]);
	for (int i = 1; i <= n; i ++) b[a[i]] = 0;
	for (int i = 1; i <= n; i ++) b[a[i]] ++;

	int l = 1, r = maxw - minw + 1, ans = -1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) {
			ans = mid - 1;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}

	printf("%d\n", ans);

	for (int i = 1; i <= n; i ++) -- b[a[i]];
}

int main() {
	for (int i = 1; i <= 1e5; i ++) for (int j = i; j <= 1e5; j += i) factor[j].push_back(i);
	int T; scanf("%d", &T);
	while (T --) solve();
	return 0;
}

D

首先对于这种题目一般直接拆贡献更加方便,我们就不用去对整棵树考虑了。

为了更方便的考虑这个问题,我们可以从期望方面入手。

我们知道从大小为 \(n\) 的集合中选奇数个数和选偶数个数的方案数是相等的。

又因为按位异或其实就是看 \(1\) 的个数,所以一个节点为 \(1\) 的期望其实就是 \(\frac{1}{2}\)

然后又由于从子节点中最深的叶子结点删他们直接距离次最终这个节点的答案才为 \(0\)

所以我们就可以很轻松的统计答案了。

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

typedef long long ll;
const ll MOD = 1e9 + 7;

void solve() {
	int n; scanf("%d", &n);
	vector<vector<int>> son(n + 1);
	vector<int> f(n + 1);
	vector<int> dep(n + 1);
	for (int i = 1, u, v; i < n; i ++) scanf("%d%d", &u, &v), son[u].push_back(v), son[v].push_back(u);
	function<int(int, int)> dfs = [&](int x, int fa) {
		int maxDep = dep[x];
		for (int y : son[x]) if (y != fa){
			dep[y] = dep[x] + 1;
			maxDep = max(maxDep, dfs(y, x));
		}
		f[x] = maxDep - dep[x] + 1;
		return maxDep;
	};
	dep[1] = 1;
	dfs(1, 1);
	ll mul = 1, ans = 0;
	for (int i = 1; i < n; i ++) mul = mul * 2 % MOD;
	for (int i = 1; i <= n; i ++) ans = (ans + mul * f[i] % MOD) % MOD;
	printf("%lld\n", ans);
}

int main() {
	int T; scanf("%d", &T);
	while (T --) solve();
	return 0;
}

挺奇妙的题,当然其实从别的方面考虑也行。