[赛记] 多校A层冲刺NOIP2024模拟赛19

图书管理 85pts

2s 1e10助我85pts

考虑正解,仍然是算贡献

这个题有一个很通用的套路:将大于某数的数看成 $ 1 $,小于这个数的数看成 $ -1 $

那么我们枚举 $ a_i $,运用上面的套路将 $ i $ 左边的前缀和算出来并开个桶记录一下端点编号之和,然后在枚举 $ i $ 右边的同时找到现在的前缀和的相反数所对应的桶中的端点编号之和,乘一下即可;

注意前缀和为 $ 0 $ 的时候需要算上 $ i $;

时间复杂度:$ \Theta(n^2) $;

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n;
long long a[500005];
long long ans;
long long t[500005];
int main() {
	freopen("book.in", "r", stdin);
	freopen("book.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		ans += 1ll * i * i * a[i];
	}
	for (int i = 1; i <= n; i++) {
		int sum = 0;
		for (int j = i - 1; j >= 1; j--) {
			if (a[j] < a[i]) sum--;
			else sum++;
			t[sum + n] += j;
			if (sum == 0) ans += 1ll * i * j * a[i];
		}
		sum = 0;
		for (int j = i + 1; j <= n; j++) {
			if (a[j] < a[i]) sum--;
			else sum++;
			if (sum == 0) ans += 1ll * i * j * a[i];
			ans += 1ll * t[-sum + n] * j * a[i];
		}
		sum = 0;
		for (int j = i - 1; j >= 1; j--) {
			if (a[j] < a[i]) sum--;
			else sum++;
			t[sum + n] -= j;
		}
	}
	cout << ans;
	return 0;
}

两棵树 30pts

直接暴搜30pts;

考虑正解,有个性质,最后剩余的连通块数为(点数-边数);

所以我们的答案为点乘点-2乘点乘边+边乘边;

然后直接粘题解了

image

时间复杂度:$ \Theta(n) $;

点击查看代码
#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
const long long mod = 998244353;
int n;
set<int> s[500005];
struct sss{
	int t, ne;
}e[500005];
int h[500005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
long long ksm(long long a, long long b) {
	long long ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
long long ans;
int d[500005];
void dfs(int x, int fa) {
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		if (u == fa) continue;
		long long sum = n - 1 - d[x] - d[u];
		auto it = s[x].lower_bound(u);
		if (it != s[x].end() && *it == u) sum++;
		ans = (ans + sum * ksm(16, mod - 2) % mod) % mod;
		dfs(u, x);
	}
}
int main() {
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	int x, y;
	for (int i = 1; i <= n - 1; i++) {
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= n - 1; i++) {
		cin >> x >> y;
		s[x].insert(y);
		s[y].insert(x);
		d[x]++;
		d[y]++;
	}
	ans = (1ll * n * (n - 1) % mod * ksm(4, mod - 2) % mod - 1ll * (n - 1) * (n - 2) % mod * ksm(4, mod - 2) % mod + mod) % mod;
	dfs(1, 0);
	cout << ans;
	return 0;
}

函数 60pts

$ \Theta(n^2) $ 居然有60pts?

一个套路:对于一个区间 $ [l, r] $,如果它们两个相反,那么它们的中点 $ mid $ 所对应的值会和 $ l, r $ 中的一个相反,所以可以依据这个来在 $ \Theta(\log n) $ 的时间复杂度内找到一对相邻的相反元素

所以对于查询操作,我们先用Trie树找到异或出来的最大值和最小值,如果它们减 $ b $ 同号就无解,否则直接用套路缩小范围即可;

注意判断两个极值和每次判断 $ mid $ 值异或完减 $ b $ 是否为 $ 0 $;

时间复杂度:$ \Theta(n (\log n + \log V)) $;

点击查看代码
#include <iostream>
#include <cstdio>
#include <bitset>
using namespace std;
int n, q;
long long a[1000005];
namespace Trie{
	int son[30000005][2], tot;
	int id[30000005];
	void add(long long x, int i) {
		int now = 0;
		for (int j = 30; j >= 0; j--) {
			int y = ((x >> j) & 1);
			if (!son[now][y]) son[now][y] = ++tot;
			now = son[now][y];
		}
		id[now] = i;
	}
	int ask_ma(long long x) {
		int now = 0;
		for (int j = 30; j >= 0; j--) {
			int y = ((x >> j) & 1);
			if (son[now][y ^ 1]) now = son[now][y ^ 1];
			else now = son[now][y];
		}
		return id[now];
	}
	int ask_mi(long long x) {
		int now = 0;
		for (int j = 30; j >= 0; j--) {
			int y = ((x >> j) & 1);
			if (son[now][y]) now = son[now][y];
			else now = son[now][y ^ 1];
		}
		return id[now];
	}
}
using namespace Trie;
int main() {
	freopen("fun.in", "r", stdin);
	freopen("fun.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		add(a[i], i);
	}
	long long x, y;
	for (int i = 1; i <= q; i++) {
		cin >> x >> y;
		int ma = ask_ma(x);
		int mi = ask_mi(x);
		if ((a[ma] ^ x) - y == 0) {
			if (ma == 1) cout << ma << '\n';
			else cout << ma - 1 << '\n';
			continue;
		}
		if ((a[mi] ^ x) - y == 0) {
			if (mi == 1) cout << mi << '\n';
			else cout << mi - 1 << '\n';
			continue;
		}
		if (1ll * ((a[ma] ^ x) - y) * ((a[mi] ^ x) - y) > 0) {
			cout << -1 << '\n';
			continue;
		}
		int l = min(ma, mi);
		int r = max(ma, mi);
		bool vis = false;
		while(r - l > 1) {
			int mid = (l + r) >> 1;
			if ((a[mid] ^ x) - y == 0) {
				vis = true;
				if (mid == 1) cout << mid << '\n';
				else cout << mid - 1 << '\n';
				break;
			}
			if (1ll * ((a[l] ^ x) - y) * ((a[mid] ^ x) - y) < 0) r = mid;
			else l = mid;
		}
		if (!vis) cout << l << '\n';
	}
	return 0;
}
posted @ 2024-11-08 16:14  Peppa_Even_Pig  阅读(17)  评论(0编辑  收藏  举报