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

这场一共就打了26pts,这要是NOIP不得完事

随机游走 10pts

首先给出结论:对儿子序列排序,cmp 函数为 sumb×wa<suma×wb,其中 sumx 代表 x 的子树内的点权和,wx 代表 x 的子树内的边权和(包括 (u,fau) 这条边), a 在待排序序列中先于 b

然后我们排序一遍儿子序列,正常 dfs 即可;

对儿子序列进行排序还是不难想的,因为我们要确定一个遍历顺序,但是对于 cmp 函数就不是太好想,赛时可能就是猜一下把(但是我没猜到)

下面我们对于这个结论进行一个证明;

首先我们要明确的是证明方向,那么我们只需证明对于 i,i+1 这两个相邻的儿子节点满足上述结论,然后依据冒泡排序即可得到证明;

考虑如果 i 先于 i+1,那么我们只考虑 i,i+1 这两个点的贡献,其它点的贡献是不变的,设 fi 表示 i 子树内到 i 的答案,那么它们两个的贡献为:

fi+fi+1+j=1i1wj×sumi+j=1iwj×sumi+1

如果 i+1 先于 i 的话,那么它们两个的贡献为:

fi+fi+1+j=1i1wj×sumi+1+(j=1i1wj+wi+1)×sumi

=fi+fi+1+j=1i1wj×sumi+1+j=1i1wj×sumi+wi+1×sumi

我们要让下式小于上式,所以:

fi+fi+1+j=1i1wj×sumi+j=1iwj×sumi+1>fi+fi+1+j=1i1wj×sumi+1+j=1i1wj×sumi+wi+1×sumi

移项可得:

sumi+1×wi<sumi×wi+1

证毕;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int n;
vector<pair<int, long long> > v[500005];
long long a[500005], w[500005], sum[500005];
void dfs(int x) {
	sum[x] = a[x];
	for (int i = 0; i < v[x].size(); i++) {
		int u = v[x][i].first;
		dfs(u);
		sum[x] += sum[u];
		w[u] += v[x][i].second;
		w[x] += w[u];
	}
}
bool cmp(pair<int, long long> a, pair<int, long long> b) {
	return sum[b.first] * w[a.first] < sum[a.first] * w[b.first];
}
long long now, ans;
void cfs(int x) {
	for (int i = 0; i < v[x].size(); i++) {
		int u = v[x][i].first;
		now += v[x][i].second;
		ans += (now * a[u]);
		cfs(u);
	}
}
int main() {
	freopen("walk.in", "r", stdin);
	freopen("walk.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	int x;
	long long y;
	for (int i = 2; i <= n; i++) {
		cin >> x >> y;
		v[x].push_back({i, y});
	}
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	dfs(1);
	for (int i = 1; i <= n; i++) {
		sort(v[i].begin(), v[i].end(), cmp);
	}
	cfs(1);
	cout << ans;
	return 0;
}

分发奖励 16pts

首先求出原树的 DFS 序,然后发现每次最多只会修改两个区间,于是我们先将这些区间绑到所在子树的根节点上,然后对树进行 DFS,每进入一个节点就将其对应的区间加入线段树中,返回时撤销,然后就做完了;

对于操作,我们可以把它理解为区间加减 1,然后这样我们直接查询全局 0 的个数即可知道答案;

对于查询,我们维护最小值以及最小值的个数,这样就间接知道了全局 0 的个数;

时间复杂度:Θ(nlogn),常数较大;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n, q;
struct sss{
	int t, ne;
}e[1000005];
int h[1000005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
vector<int> v[500005];
int dcnt;
pair<int, int> a[500005];
int ans[500005];
bool vis[500005];
namespace SEG{
	inline int ls(int x) {
		return x << 1;
	}
	inline int rs(int x) {
		return x << 1 | 1;
	}
	struct sss{
		int l, r, mi, sum, lz;
	}tr[3000005];
	inline void push_up(int id) {
		if (tr[ls(id)].mi < tr[rs(id)].mi) {
			tr[id].sum = tr[ls(id)].sum;
		} else if (tr[ls(id)].mi > tr[rs(id)].mi) {
			tr[id].sum = tr[rs(id)].sum;
		} else {
			tr[id].sum = tr[ls(id)].sum + tr[rs(id)].sum;
		}
		tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
	}
	inline void push_down(int id) {
		if (tr[id].lz != 0) {
			tr[ls(id)].lz += tr[id].lz;
			tr[rs(id)].lz += tr[id].lz;
			tr[ls(id)].mi += tr[id].lz;
			tr[rs(id)].mi += tr[id].lz;
			tr[id].lz = 0;
		}
	}
	void bt(int id, int l, int r) {
		tr[id].l = l;
		tr[id].r = r;
		if (l == r) {
			tr[id].sum = 1;
			return;
		}
		int mid = (l + r) >> 1;
		bt(ls(id), l, mid);
		bt(rs(id), mid + 1, r);
		push_up(id);
	}
	void add(int id, int l, int r, int d) {
		if (tr[id].l >= l && tr[id].r <= r) {
			tr[id].mi += d;
			tr[id].lz += d;
			return;
		}
		push_down(id);
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (l <= mid) add(ls(id), l, r, d);
		if (r > mid) add(rs(id), l, r, d);
		push_up(id);
	}
}
void afs(int x) {
	a[x].first = ++dcnt;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		afs(u);
	}
	a[x].second = dcnt;
}
void dfs(int x) {
	for (int i = 0; i < v[x].size(); i++) {
		SEG::add(1, a[v[x][i]].first, a[v[x][i]].second, 1);
	}
	if (SEG::tr[1].mi == 0) ans[x] = n - SEG::tr[1].sum - 1;
	else ans[x] = n - 1;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		dfs(u);
	}
	for (int i = 0; i < v[x].size(); i++) {
		SEG::add(1, a[v[x][i]].first, a[v[x][i]].second, -1);
	}
}
int main() {
	freopen("reward.in", "r", stdin);
	freopen("reward.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q;
	int x;
	for (int i = 2; i <= n; i++) {
		cin >> x;
		add(x, i);
	}
	int y;
	for (int i = 1; i <= q; i++) {
		cin >> x >> y;
		v[x].push_back(y);
		v[y].push_back(x);
		vis[x] = vis[y] = true;
	}
	for (int i = 1; i <= n; i++) {
		if (vis[i]) v[i].push_back(i);
	}
	afs(1);
	SEG::bt(1, 1, dcnt);
	dfs(1);
	for (int i = 1; i <= n; i++) cout << max(0, ans[i]) << ' ';
	return 0;
}

卡路里 0pts

这个题有点意思;

考虑我们要求的是一段区间,满足这段区间 [l,r] 内点权减去 l,r 的距离最大;

所以我们获得了一个 Θ(m2n) 的暴力;

考虑怎么优化这个东西,发现枚举区间找最大值没什么前途,所以我们考虑每个点的贡献

考虑到每种点只能选一个,所以我们可以处理出每个点的管辖区间,即每个点在一段区间 [l,r] 内为最大值,我们需要找出这个区间,这个可以单调栈一次求出,然后再这个区间内加上这个点的点权;

但是又考虑到原数据中有重复的点权,如果直接这样求的话我们两边的区间会被算多次,所以我们考虑两次单调栈,一次正序维护严格,一次逆序维护不严格(当然反过来也行),这样就解决了这个问题;

然后我们就转化成了有若干个区间加的形式,求每个区间和的最大值;

我们可以遍历每个区间,这个是 Θ(m2) 的,现在关键是怎样 Θ(1) 处理区间加,不难想到二维差分

具体来讲,我们将 l 看作 x 轴,将 r 看作 y 轴,然后我们区间加就是矩形加,查询就是单点查,直接上差分即可;

时间复杂度:Θ(m2+nm)

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
long long d[5005], a[5005][205], su[5005], sum[5005][5005];
int s[5005], cnt;
int l[5005], r[5005];
int main() {
	freopen("calorie.in", "r", stdin);
	freopen("calorie.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for (int i = 2; i <= m; i++) {
		cin >> d[i];
		su[i] = su[i - 1] + d[i];
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> a[i][j];
		}
	}
	for (int j = 1; j <= n; j++) {
		cnt = 0;
		s[0] = 0;
		for (int i = 1; i <= m; i++) {
			while(cnt > 0 && a[i][j] > a[s[cnt]][j]) cnt--;
			l[i] = s[cnt] + 1;
			s[++cnt] = i;
		}
		cnt = 0;
		s[0] = m + 1;
		for (int i = m; i >= 1; i--) {
			while(cnt > 0 && a[i][j] >= a[s[cnt]][j]) cnt--;
			r[i] = s[cnt] - 1;
			s[++cnt] = i;
		}
		for (int i = 1; i <= m; i++) {
			sum[l[i]][i] += a[i][j];
			sum[l[i]][r[i] + 1] -= a[i][j];
			sum[i + 1][i] -= a[i][j];
			sum[i + 1][r[i] + 1] += a[i][j];
		}
	}
	long long ans = 0;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= m; j++) {
			sum[i][j] += (sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1]);
			if (i <= j) ans = max(ans, sum[i][j] - (su[j] - su[i]));
		}
	}
	cout << ans;
	return 0;
}
posted @   Peppa_Even_Pig  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示