EPIC Institute of Technology Round August 2024 (Div. 1 + Div. 2)

https://codeforces.com/contest/2002

D1 — DFS Checker (Easy Version) and D2 — DFS Checker (Hard Version)

解析:
找一种简单以维护的方法判断dfs程序的正确性:
根据树的性质容易知道:对于节点 u 和其子节点 v,id[u] 为节点 u 在dfs序中的位置,size[u] 为以u为根的子树的大小。不难发现 [ id[v],id[v]+size[v]-1 ] 包含于 [ id[u],id[u]+size[u]-1 ],因此可以通过维护这两类区间来判断dfs序的正确性。

首先需要一个set<int>son[i],表示以 i 为父节点的子节点的在dfs序中的位置,通过以下代码便可判断出dfs序的正确性。

int check(int x) {
	return son[x].empty() ? 1 : (id[x] < *son[x].begin() &&
             *--son[x].end() + siz[p[*--son[x].end()]] <= id[x] + siz[x]);
}

完整代码:

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <utility>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <math.h>
#include <map>
#include <sstream>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <stdio.h>
#include <tuple>
using namespace std;
/*
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
*/
typedef long long LL;
//#define int LL
#define ld long double
const LL INF = 0x3f3f3f3f3f3f3f3f;
typedef unsigned long long ULL;
typedef pair<long long, long long> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const int inf = 0x3f3f3f3f;
const LL Mod = 998244353;
const ld eps = 1e-12;
const int N = 3e5 + 10, M = 5e5 + 10;
int n, Q;
int fa[N], p[N], id[N], siz[N];
set<int>son[N];
int check(int x) {
	return son[x].empty() ? 1 : (id[x] < *son[x].begin() && *--son[x].end() + siz[p[*--son[x].end()]] <= id[x] + siz[x]);
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> Q;
		for (int i = 1; i <= n; i++) {
			siz[i] = 1;
			son[i].clear();
		}
		for (int i = 2; i <= n; i++) {
			cin >> fa[i];
		}
		for (int i = n; i > 1; i--) {
			siz[fa[i]] += siz[i];
		}
		for (int i = 1; i <= n; i++) {
			cin >> p[i];
			son[fa[p[i]]].insert(i), id[p[i]] = i;
		}
		int cnt = 0;
		for (int i = 1; i <= n; i++) {
			cnt += check(i);
		}
		int i, j;
		while (Q--) {
			cin >> i >> j;
			int x = p[i], y = p[j];
			set<int>s;
			s.insert(x), s.insert(y), s.insert(fa[x]), s.insert(fa[y]);
			for (auto x : s)
				if (x)cnt -= check(x);
			son[fa[x]].erase(i), son[fa[y]].erase(j);
			swap(p[i], p[j]), swap(id[x], id[y]);
			son[fa[x]].insert(j), son[fa[y]].insert(i);
			for (auto x : s)
				if (x)cnt += check(x);
			if (cnt == n) {
				cout << "YES" << endl;
			}
			else {
				cout << "NO" << endl;
			}
		}
	}
	return 0;
}
/*
3
3 3
1 1
1 2 3
2 3
3 2
1 3
7 4
1 1 2 2 3 3
1 2 3 4 5 6 7
3 5
2 5
3 7
4 6
5 4
1 1 3 4
2 3 4 5 1
5 1
4 5
3 4
2 3


*/

E. Cosmic Rays

解析:

问题要求对每个前缀给出答案,这暗示了一种增量解决方案。

为了将一个新对添加到当前前缀中,我们需要以某种方式处理新块与旧块的合并。因此,我们应使用某种结构来存储关于最后一个块随时间的信息。

具体地,我们使用栈来跟踪所有成为最后一个块的块。对于每个块,我们保留两个值:它的颜色 c 和它的寿命 l(块消失所需的时间)。

插入一个新块时,我们将弹出所有会被当前块遮蔽的块(即寿命短于当前块的块),并合并具有相同颜色的块。当合并两个长度分别为 x 和 z 的块,且它们之间块的最大寿命为 y 时,应满足 y≤min(x,z),新块将具有 x+z−y 的寿命。

有关更多详细信息,请参考解决方案代码。

还存在使用有序集合或堆的 O(nlog⁡n)解决方案。

时间复杂度:O(n)

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <utility>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <math.h>
#include <map>
#include <sstream>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <stdio.h>
#include <tuple>
using namespace std;
/*
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
*/
typedef long long LL;
#define int LL
#define ld long double
const LL INF = 0x3f3f3f3f3f3f3f3f;
typedef unsigned long long ULL;
typedef pair<long long, long long> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const int inf = 0x3f3f3f3f;
const LL Mod = 998244353;
const ld eps = 1e-12;
const int N = 3e5 + 10, M = 5e5 + 10;
int n;

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		cin >> n;
		vector<PII>stk;
		int ans = 0;
		vector<int>out;
		for (int i = 1; i <= n; i++) {
			int a, b;
			cin >> a >> b;
			int len = 0;
			while (!stk.empty() && (a > 0 || stk.back().second == b)) {
				if (stk.back().second == b) {
					len += stk.back().first;
					stk.pop_back();
				}
				else {
					int t = min(a, stk.back().first);
					len += t;
					a -= t;
					stk.back().first -= t;
					if (stk.back().first == 0) {
						stk.pop_back();
					}
				}
			}
			len += a;
			ans += a;
			stk.push_back({ len,b });
			//cout << "____________" << len << " " << b << endl;
			out.push_back(ans);
		}
		for (int x : out) {
			cout << x << " ";
		}
		cout << endl;
	}
	return 0;
}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值