首页 > 其他分享 >[赛记] NOIP2024加赛5

[赛记] NOIP2024加赛5

时间:2024-11-20 09:08:40浏览次数:1  
标签:赛记 int siz tr ans 加赛 id NOIP2024 first

暴力操作(opt)30pts

这个错解可反悔贪心30pts;

考虑正解,我们只需考虑前 $ \frac n2 + 1 $ 小的数即可;

考虑二分出一个中位数 $ mid $,那么我们要让大于它的都用最小的代价变小;

考虑如何求这个最小的代价,因为 $ \lfloor \frac{\lfloor \frac ab \rfloor}{c} \rfloor = \lfloor \frac{ab}{c} \rfloor $,我们可以预处理出所有对于除以一个数 $ i $ 的最小代价,这个可以调和级数处理,即 $ c_{i \times j} = \min(c_{i \times j}, c_i + c_j) $,然后处理完以后维护一个后缀 $ \min $ 即可;

我们还要考虑除成 $ 0 $ 的情况,我们把这种情况的最小值存到 $ c_{n + 1} $ 中,那么我们可以得到 $ c_{n + 1} = \min_{i = 1}^{n}(c_{n + 1}, c_i + c_{\lfloor \frac ni \rfloor + 1}) $,最后再维护一个后缀 $ \min $ 即可;

然后直接 check 就没了;

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

点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
long long n, m, k;
long long a[500005], c[500005];
bool ck(int x) {
	long long ans = 0;
	for (int i = 1; i <= n / 2 + 1; i++) {
		if (a[i] > x) ans += c[a[i] / (x + 1) + 1];
	}
	return (ans <= k);
}
int main() {
	freopen("opt.in", "r", stdin);
	freopen("opt.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= m; i++) {
		cin >> c[i];
	}
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j * i <= m; j++) {
			c[i * j] = min(c[i * j], c[i] + c[j]);
		}
	}
	for (int i = m - 1; i >= 1; i--) c[i] = min(c[i], c[i + 1]);
	c[m + 1] = 2e9;
	for (int i = 2; i <= m; i++) {
		c[m + 1] = min(c[m + 1], c[i] + c[m / i + 1]);
	}
	for (int i = m; i >= 1; i--) {
		c[i] = min(c[i], c[i + 1]);
	}
	int l = 0;
	int r = m;
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if (ck(mid)) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	cout << ans;
	return 0;
}

异或连通(xor)0pts

重点在于想到线段树分治

然后直接考虑线段树分治(这个题直接 $ Trie $ 上分治就行),因为发现每条边在所有询问中出现的都是若干段不超过 $ \log k $ 段区间中的;

考虑首先建出 $ 01Trie $,那么这些询问从小到大在这个 $ 01Trie $ 上是连续的;

然后考虑一条边对于那些询问有贡献,可以发现,如果二进制位上这条边的 $ c $ 为 $ 0 $,而 $ Trie $ 上为 $ 1 $ ,异或起来就是 $ 1 $ ,$ k $ 这一位为 $ 1 $ 时可能成立,为 $ 0 $ 不成立,所以依据这个我们就可以直接给子树打上标记,然后直接 $ Trie $ 上分治即可;

时间复杂度:$ \Theta(n \log n \log V) $,其中 $ V $ 是值域;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <map>
using namespace std;
int n, m, q, k;
int x[500005], y[500005], c[500005], fa[500005], siz[500005], s[500005];
int find(int x) {
	if (x == fa[x]) return fa[x];
	else return find(fa[x]);
}
int tot, rt;
stack<pair<pair<int, int>, int> > sk;
long long ans;
map<int, long long> mp;
int p[35];
inline void merge(int x, int y) {
	x = find(x);
	y = find(y);
	if (siz[x] < siz[y]) {
		sk.push({{y, siz[y]}, x});
		if (x == y) return;
		ans -= (1ll * siz[x] * (siz[x] - 1) / 2);
		ans -= (1ll * siz[y] * (siz[y] - 1) / 2);
		siz[y] += siz[x];
		ans += (1ll * siz[y] * (siz[y] - 1) / 2);
		fa[x] = y;
	} else {
		sk.push({{x, siz[x]}, y});
		if (x == y) return;
		ans -= (1ll * siz[x] * (siz[x] - 1) / 2);
		ans -= (1ll * siz[y] * (siz[y] - 1) / 2);
		siz[x] += siz[y];
		ans += (1ll * siz[x] * (siz[x] - 1) / 2);
		fa[y] = x;
	}
}
namespace Trie{
	struct sss{
		int ls, rs, id;
		vector<int> v;
	}tr[5000005];
	void add(int now, int &id, int d) {
		if (!id) id = ++tot;
		if (!now) {
			tr[id].id = d;
			return;
		}
		if ((d >> (now - 1)) & 1) add(now - 1, tr[id].ls, d);
		else add(now - 1, tr[id].rs, d);
	}
	void add_e(int now, int id, int x, int d) {
		if (!id || !now) return;
		int v = ((d >> (now - 1)) & 1);
		if (p[now]) {
			if (v) {
				tr[tr[id].ls].v.push_back(x);
				add_e(now - 1, tr[id].rs, x, d);
			} else {
				tr[tr[id].rs].v.push_back(x);
				add_e(now - 1, tr[id].ls, x, d);
			}
		} else {
			if (v) add_e(now - 1, tr[id].ls, x, d);
			else add_e(now - 1, tr[id].rs, x, d);
		}
	}
	void dfs(int now, int id) {
		if (!id) return;
		for (int i = 0; i < tr[id].v.size(); i++) {
			int o = tr[id].v[i];
			merge(x[o], y[o]);
		}
		if (!now) {
			mp[tr[id].id] = ans;
			for (int i = 0; i < tr[id].v.size(); i++) {
				pair<pair<int, int>, int> t = sk.top();
				sk.pop();
				if (t.first.first == t.second) continue;
				ans -= (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
				siz[t.first.first] = t.first.second;
				fa[t.second] = t.second;
				ans += (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
				ans += (1ll * siz[t.second] * (siz[t.second] - 1) / 2);
			}
			return;
		}
		dfs(now - 1, tr[id].ls);
		dfs(now - 1, tr[id].rs);
		for (int i = 0; i < tr[id].v.size(); i++) {
			pair<pair<int, int>, int> t = sk.top();
			sk.pop();
			if (t.first.first == t.second) continue;
			ans -= (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
			siz[t.first.first] = t.first.second;
			fa[t.second] = t.second;
			ans += (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
			ans += (1ll * siz[t.second] * (siz[t.second] - 1) / 2);
		}
	}
}
using namespace Trie;
int main() {
	freopen("xor.in", "r", stdin);
	freopen("xor.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> q >> k;
	for (int i = 1; i <= 32; i++) {
		p[i] = ((k >> (i - 1)) & 1);
	}
	for (int i = 1; i <= m; i++) {
		cin >> x[i] >> y[i] >> c[i];
	}
	for (int i = 1; i <= q; i++) {
		cin >> s[i];
		add(32, rt, s[i]);
	}
	for (int i = 1; i <= m; i++) {
		add_e(32, rt, i, c[i]);
	}
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
		siz[i] = 1;
	}
	dfs(32, 1);
	for (int i = 1; i <= q; i++) {
		cout << mp[s[i]] << '\n';
	}
	return 0;
}

民主投票(election)25pts

呵呵,想不到

然后直接二分答案,二分的是每个点最多被投的票数(当票数一样时),然后得到一个答案 $ res $;

考虑对于一个点 $ x $ ,如果 $ siz_x - 1 < res $,那么不可以,如果 $ siz_x - 1 > res $,那么可以,如果相等,考虑 $ res - 1 $ 对于这个点合不合法,我们就一直向上传到根,如果此时根能接收到这个票,那就可行,否则不行;

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

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int t;
int n;
int fa[5000005];
struct sss{
	int t, ne;
}e[5000005];
int h[5000005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
int siz[5000005];
void dfs(int x) {
	siz[x] = 0;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		dfs(u);
		siz[x] += siz[u] + 1;
	}
}
int f[5000005];
int ans[5000005];
void afs(int x, int y) {
	f[x] = 0;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		afs(u, y);
		f[x] += f[u];
	}
	f[x] = max(0, f[x] - y) + 1;
}
bool ck(int x) {
	afs(1, x);
	return (f[1] == 1);
}
void cfs(int x, int y) {
	if (siz[x] == y) {
		ans[x] = 1;
		return;
	}
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		if (f[u] > 1) cfs(u, y);
	}
}
int main() {
	freopen("election.in", "r", stdin);
	freopen("election.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	while(t--) {
		cin >> n;
		for (int i = 2; i <= n; i++) {
			cin >> fa[i];
			add(fa[i], i);
		}
		dfs(1);
		int l = 1;
		int r = n;
		int res = 0;
		while(l <= r) {
			int mid = (l + r) >> 1;
			if (ck(mid)) {
				res = mid;
				r = mid - 1;
			} else l = mid + 1;
		}
		for (int i = 1; i <= n; i++) {
			if (siz[i] > res) ans[i] = 1;
			else ans[i] = 0;
		}
		afs(1, res - 1);
		if (f[1] == 2) { //根自己一个 + 上传的一个
			cfs(1, res);
		}
		for (int i = 1; i <= n; i++) {
			cout << ans[i];
		}
		cout << '\n';
		for (int i = 1; i <= n; i++) h[i] = 0;
		cnt = 0;
	}
	return 0;
}

标签:赛记,int,siz,tr,ans,加赛,id,NOIP2024,first
From: https://www.cnblogs.com/PeppaEvenPig/p/18556057

相关文章

  • [赛记] 多校A层冲刺NOIP2024模拟赛24
    选取字符串60pts直接暴力60pts;这题难点在于读懂题把。。。考虑建出$KMP$树,然后在其中选出$k$个数,他们的$LCA$的深度的平方和就是这个答案,然后简单统计一下即可;具体地,把$KMP$树建出来,然后求每$k$个点的$LCA$的深度的平方和即可,最后乘上方案数(总的减去......
  • 多校A层冲刺NOIP2024模拟赛24
    选取字符串建出失配树以后直接dp就好了。但场上现推的kmp……点此查看代码#include<bits/stdc++.h>usingnamespacestd;#definerep(i,s,t,p)for(inti=s;i<=t;i+=p)#definedrep(i,s,t,p)for(inti=s;i>=t;i-=p)#ifdefLOCALFILE*InFile=freope......
  • NOIP2024加赛6
    一签三计数,罚坐了。草莓简单贪心,随便贪就过了。点此查看代码#include<bits/stdc++.h>usingnamespacestd;#definerep(i,s,t,p)for(inti=s;i<=t;i+=p)#definedrep(i,s,t,p)for(inti=s;i>=t;i-=p)#ifdefLOCAL FILE*InFile=freopen("in.in","r......
  • 多校A层冲刺NOIP2024模拟赛24
    多校A层冲刺NOIP2024模拟赛24\(T1\)A.选取字符串\(100pts\)考虑建出失配树,然后等价于询问\(\sum\limits_{S\sube\{0,1,2,\dots,n\},|S|=k}dep_{\operatorname{LCA}\{S\}}^{2}\)。不妨从\(\operatorname{LCA}\)的角度考虑,统计\(x\)能作为多少个\(|S|\)......
  • [71] (多校联训) A层冲刺NOIP2024模拟赛24
    bydT3放道这种题有什么深意吗flowchartTB A(选取字符串) styleAcolor:#ffffff,fill:#00c0c0,stroke:#ffffff确实是签,但是一直在想组合意义,最后因为没提前处理逆元遗憾离场了,赛后看题解发现的确是往树上转化更简单点赛时的组合意义代码没过#include<bits/stdc++.h>us......
  • 『模拟赛』NOIP2024加赛6
    Rank大奋场,T3没切有点菜A.草莓和前天多校T3很像,所以一眼鉴定为贪心,从大到小选比从小到大选一眼优,代价一样时横竖无所谓先后,然后sort一遍就做完了,复杂度\((n+m)\log(n+m)\)。10min切的。点击查看代码#include<bits/stdc++.h>#definefo(x,y,z)for(registerint......
  • NOIP2024加赛6
    NOIP2024加赛6\(T1\)P323.草莓\(60pts\)部分分\(60pts\)先将\(\{x\},\{y\}\)降序排序,状态转移方程为\(f_{i,j}=\min(f_{i-1,j}+x_{i}(j+1),f_{i,j-1}+y_{j}(i+1))\),边界为\(f_{0,0}=0\),最终有\(f_{n-1,m-1}\)即为所求。若费用提前计算则需要将\(\{x\}......
  • [2024.11.18]NOIP2024模拟赛#23
    赛时T1题面实在太奇怪,结合样例看了好久才看懂。看懂以后发现应该就是简单神秘结论题。简单写了一会就过了样例,发现没给大样例,就扔了。T2第一眼感觉还是结论题,但是如果发现每个点能保证只覆盖一次的话就能做到\(\mathcal{O}(nm)\)。然后开始写,写完不过样例,发现题目让先输入......
  • [赛记] 多校A层冲刺NOIP2024模拟赛23
    字符串构造机100pts原题,见[赛记]多校A层冲刺NOIP2024模拟赛01【衡中】T1;忍者小队60pts赛时最后想出来个$\Theta(n^2\logn)$的DP,所以60pts;对于这个DP,直接用map维护一下所有lcm的状态转移即可;点击查看代码#include<iostream>#include<cstdio>#include<vect......
  • NOIP2024加赛5
    暴力操作(opt)拜谢丁真首先题目有一个很明显的性质:我们肯定只会对前\(\cfrac{n+1}{2}\)个数进行操作使它变小。最后的答案很明显没看出来具有二分答案的性质,考虑怎么check。实则就是要判断前\(\cfrac{n+1}{2}\)个数是否都能\(\lemid\)。我们可以方便的找出\(a_i\)变......