Northern Eurasia Finals Online 2020 队内训练

这篇博客探讨了树形结构在计算机科学中的应用,包括平衡树的构造、颜色填充问题、数值计算以及查找性能优化。文章通过实例展示了如何利用二叉树、平衡树和路径查找来解决实际问题,如分配权值、寻找特定路径和计算函数执行次数。此外,还涉及了数据结构优化和递归算法在处理树状数据时的作用。

A. Almost Balanced Tree

  • 考虑一种做法,先分配权值为2的点,每次都将权值为2的点数二等分,若是偶数就往下分,否则自身先占用一个再往下分
  • 权值为2的点分配完后,对整一棵树的空位分配权值为1的点
  • 若不够分,则无解
  • 若多出来了,同样将1的个数二分往下
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
ll gcd(ll x, ll y) {
	if (y == 0) return x;
	return gcd(y, x % y);
}
int n, m;
int num[N] = { 0 }, id[N] = { 0 }, rid[N];
bool vis[N] = { false };
void dfs(int x, int cnt) {
	vis[x] = true;
	if (cnt & 1) num[x] = 2, --cnt;
	if (cnt) {
		dfs(x << 1, cnt >> 1);
		dfs(x << 1 | 1, cnt >> 1);
	}
}
void dfs1(int x, int& tot) {
	if (num[x] == 0) num[x] = 1, ++tot;
	if (vis[x << 1]) dfs1(x << 1, tot);
	if (vis[x << 1 | 1]) dfs1(x << 1 | 1, tot);
}
void dfs2(int x, int tot) {
	vis[x] = true;
	if (!num[x] && tot) num[x] = 1, --tot;
	//	printf("%d %d\n", x, num[x]);
	if (tot > 0) {
		int tmp = tot >> 1;
		dfs2(x << 1, tmp);
		dfs2(x << 1 | 1, tot - tmp);
	}
}
void dfs3(int x, int& tot) {
	if (num[x]) {
		id[x] = ++tot;
		rid[tot] = x;
	}
	if (vis[x << 1]) dfs3(x << 1, tot);
	if (vis[x << 1 | 1]) dfs3(x << 1 | 1, tot);
}
int main() {
	int x, y;
	scanf("%d %d", &x, &y);
	dfs(1, y);
	int tot = 0;
	dfs1(1, tot);
	if (tot > x) puts("-1");
	else {
		x -= tot;
		dfs2(1, x);
		int tmp = 0;
		dfs3(1, tmp);
		for (int i = 1; i <= tmp; ++i) {

			int u = rid[i];
			printf("%d %d %d\n", num[u], id[u << 1], id[u << 1 | 1]);
		}
	}
	return 0;
}


C. Color the Tree

  • 对于子结点来回反复刷,乱搞
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 3e5 + 10;
const int M = 1e7 + 10;
const int mod = 1e9 + 7, inf = 0x3f3f3f3f;
ll gcd(ll x, ll y) { if (y == 0) return x; return gcd(y, x % y);}
int head[N], cntE = 0;
struct edge {
	int next, to, w;
}e[M];
void add(int u, int v, int w = 0) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
int n, m;
vector<int> dfs(int x) {
	vector<int>ans;
	for (int i = head[x]; ~i; i = e[i].next) {
		int u = e[i].to;
		vector<int>tmp = ans, now = dfs(u);
		int op = 1;
		for (auto v : now) {
			op ^= 1;
			tmp.push_back(v);
			if (op) tmp.insert(tmp.end(), ans.begin(), ans.end());
			else tmp.insert(tmp.end(), ans.rbegin(), ans.rend());
		}
		ans = tmp;
	}
	ans.insert(ans.begin(), x);
	return ans;
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
	rd(n);
	memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
	for (int i = 2; i <= n; ++i) {
		int x; rd(x);
		add(x, i);
	}
	vector<int>ans = dfs(1);
	printf("%d\n", ans.size() - 1);
	for (int i = 1; i < ans.size(); ++i) {
		printf("%d ", ans[i]);
	}
	puts("");
	return 0;
}


E. Easy Measurements

  • 题目要求
    ab+cd=bd \frac{a}{b}+\frac{c}{d}=\frac{b}{d} ba+dc=db
  • 化简得 a=b∗(b−c)da=\frac{b*(b-c)}{d}a=db(bc)
  • 由于 a>0a>0a>0 ,有 0<c<b0<c<b0<c<b
  • 所以就是求有多少个 ccc 满足算出来的 aaa 是整数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e2 + 10;
const int INF = 0x3f3f3f3f;
ll gcd(ll x, ll y) {
	if (y == 0) return x;
	return gcd(y, x % y);
}
int main() {
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		ll b, d;scanf("%lld %lld", &b, &d);
		if (b % d == 0) {
			printf("%d\n", b - 1);
			continue;
		}
		ll tmp = gcd(b, d);
		d /= tmp;
		ll ans = b / d;
		if (b % d == 0) --ans;
		printf("%lld\n", ans);
	}
}


K. Kate’s 2021 Celebration

  • 签到题
  • 题目给出nnn个气球包,每个包中都含有标记不同数字的气球和相应的价格。
  • 要找出包含两个222一个111一个000的气球包,并且其价值是最便宜的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e2 + 10;
const int INF = 0x3f3f3f3f;
int main() {
	int n;
	
	scanf("%d", &n);
	int minn = INF, ans = -1;
	for (int i = 1; i <= n; ++i) {
		int x;
		char s[N];
		scanf("%d %s", &x, s);
		int len = strlen(s);
		int sum0 = 0, sum2 = 0, sum1 = 0;
		for (int j = 0; j < len; ++j) {
			if (s[j] == '0')
				++sum0;
			else if (s[j] == '1')
				++sum1;
			else if (s[j] == '2')
				++sum2;
		}
		if (sum0 >= 1 && sum1 >= 1 && sum2 >= 2) {
			if (x < minn) {
				minn = x;
				ans = i;
			}
		}
	}
	if (ans == -1)
		puts("0");
	else
		printf("%d\n", ans);
	
}

L.Lookup Performance

  • 题意就是求题目所给的函数执行了多少次
  • 现在需要对题目所给的查询区间的 L,RL,RL,R 都找到一个关键的节点,满足这个节点往左下和右下走了之后就会停止这个函数
  • 可以得出,总会存在一个点,往两个儿子走了之后就会暂停这个函数
  • 至于怎么求这个关键的点,根据这个函数要求,比如求 LLL 这个的关键点,需要求出一个点 uuu 满足 wu<Lw_u<Lwu<L 并且求出一个点 vvv 满足 wv≥Lw_v\ge LwvL ,而这个关键点就是 lca(u,v)lca(u,v)lca(u,v)
  • 对于 RRR 同样的原理,求出一个点 uuu 满足 wu≤Rw_u\le RwuR 并且求出一个点 vvv 满足 wv>Rw_v> Rwv>R ,而这个关键点就是 lca(u,v)lca(u,v)lca(u,v)
  • 先说明一个结论,对于到 L,RL,RL,R 两个关键点的路径中,都会将函数向两个儿子结点执行一次,所以对于此路径中的每一个结点,函数都在这个结点上算上两次。
  • 所以每次查询的从根到关键点的路径长度是 h(u)+h(v)−h(lca(u,v))h(u)+h(v)-h(lca(u,v))h(u)+h(v)h(lca(u,v)),u,vu,vu,vL,RL,RL,R 对应的关键点 ,所以每个结点向下执行两次为 路径长度*2,最后再算上一开始的根结点的那份 1,答案便是 (h(u)+h(v)−h(lca(u,v)))∗2+1(h(u)+h(v)-h(lca(u,v)))*2+1(h(u)+h(v)h(lca(u,v)))2+1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 3e5 + 10;
const int M = 1e7 + 10;
const int mod = 1e9 + 7, inf = 0x3f3f3f3f;
ll gcd(ll x, ll y) { if (y == 0) return x; return gcd(y, x % y);}
int head[N], cntE = 0;
struct edge {
	int next, to, w;
}e[M];
void add(int u, int v, int w = 0) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
int n, m;
struct node {
	int w, id;
	bool friend operator<(const node& a, const node& b) {
		return a.w < b.w;
	}
	node(int w = 0, int id = 0) :w(w), id(id) {}
}a[N];
int fa[N][25] = { 0 }, h[N], lg[N];
void dfs(int x, int fx, int dep) {
	fa[x][0] = fx;
	h[x] = dep;
	for (int i = head[x]; ~i; i = e[i].next) {
		int v = e[i].to;
		dfs(v, x, dep + 1);
	}
}
void init() {
	dfs(1, 0, 1);
	for (int j = 1; j <= 20; ++j) {
		for (int i = 1; i <= n; ++i) {
			fa[i][j] = fa[fa[i][j - 1]][j - 1];
		}
	}
	lg[0] = 0;
	for (int i = 1; i <= N - 10; ++i) {
		lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	}
}
int lca(int u, int v) {
	if (h[u] < h[v]) swap(u, v);
	for (int i = 0, tmp = h[u] - h[v]; i <= 19; ++i) {
		if ((1 << i) & tmp) u = fa[u][i];
	}
	if (u == v) return u;
	for (int i = lg[h[u]] - 1; i >= 0; --i) {
		if (fa[u][i] != fa[v][i]) {
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
	rd(n);
	memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
	for (int i = 1; i <= n; ++i) {
		int x, y; rd(x), rd(y), rd(a[i].w);
		a[i].id = i;
		if (x) add(i, x);
		if (y) add(i, y);
	}
	init();
	sort(a + 1, a + 1 + n);
	rd(m);
	while (m--) {
		int l, r; rd(l), rd(r);
		if (r<a[1].w || l>a[n].w) {
			puts("1");
			continue;
		}
		int tl, tr;
		if (l <= a[1].w) tl = 0;
		else {
			int tmp = lower_bound(a + 1, a + 1 + n, node(l, 0)) - a;
			tl = lca(a[tmp - 1].id, a[tmp].id);
		}
		if (r >= a[n].w) tr = 0;
		else {
			int tmp = upper_bound(a + 1, a + 1 + n, node(r, 0)) - a;
			tr = lca(a[tmp - 1].id, a[tmp].id);
		}
		int fx = lca(tl, tr);
		int ans = (h[tl] + h[tr] - h[fx]) * 2 + 1;
		printf("%d\n", ans);
	}
	return 0;
}


M. Miser

签到题

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 10;
vector<int>vec[N];
int num[N] = { 0 };
int main() {
	int n;scanf("%d", &n);
	for (int i = 1;i <= n;++i) {
		int k;scanf("%d", &k);
		while (k--) {
			int x;scanf("%d", &x);
			vec[i].push_back(x);
		}
	}
	int ans = 0;
	int maxn = 0;
	for (int i = n;i > 0;--i) {
		maxn = 0;
		for (auto v : vec[i]) {
			maxn = max(maxn, num[v]);
		}
		++maxn;
		for (auto v : vec[i]) {
			num[v] = maxn;
		}
	}
	printf("%d\n", maxn);
}

<think> 我们面对的问题:读取2001-2020年每年一个的积雪数据文件,并计算距平。 文件路径格式:'/online1/hjwang_group/zhoufang/Data/MODIS_Terra_Snow-Cover-Daily-L3-Global-0.05Deg-CMG_Version-61/Eurasia/MOD10C1.A2001.061.Day_CMG_Snow_Cover.Eurasia.0.5.nc' 其中年份在文件名中变化(A2001,A2002,...,A2020) 距平(Anomaly)是指每个年份的值相对于长期平均(这里取2001-2020年)的偏差。 步骤: 1. 读取所有文件(20个),每个文件代表一年的积雪数据。 2. 计算这20年的平均值(即气候态)。 3. 对每一年,用该年的数据减去平均值,得到距平。 我们将使用Python中的xarray库来处理NetCDF文件(.nc格式)。xarray非常适合处理多维数组,特别是气候和气象数据。 注意:由于每个文件可能包含多个时间层(虽然文件名是“Daily”,但这里每年一个文件,可能这个文件已经是一年的平均或者是一年的某个统计量?),我们需要查看数据的具体结构。但根据文件名,我们猜测每个文件包含了一年的日积雪数据。然而,问题要求计算距平,通常距平是针对相同时间段的(比如每年同一时间段的平均)。但是,问题没有明确是计算年距平还是日距平?由于每年只有一个文件,且文件名中包含了年份,我们假设每个文件代表该年的年平均积雪覆盖数据(或者该年的某个统计量)。因此,我们计算的是年际变化,即每年的年平均积雪相对于20年平均的距平。 但是,如果每个文件包含的是日数据,那么我们需要先计算年平均,然后再计算距平。不过,文件名中有一个关键词是“Day_CMG_Snow_Cover”,所以很可能是日数据。因此,我们需要先按年求平均(即每个文件内对时间求平均),得到20个年平均数据,然后计算20年的平均(气候态),最后用每一年的年平均减去气候态得到该年的距平。 具体步骤: a. 读取一个文件,查看其变量和维度。假设变量名为'snow_cover'(需要根据实际数据确定)。 b. 对每个文件,计算该文件内所有时间步的平均(如果该文件包含多个时间步)。 c. 将20年的年平均数据组合成一个数据集。 d. 计算20年的气候态(平均)。 e. 计算每年的距平(该年年平均 - 气候态)。 如果每个文件已经是一年的平均,则跳过步骤b,直接进行c。 由于我们无法查看实际数据,这里假设每个文件包含多个时间步(日数据)。因此,我们需要对每个文件求平均。 代码框架: 1. 导入必要的库 2. 生成2001-2020年的文件路径列表 3. 读取每个文件,并计算该年的年平均(对时间维求平均) 4. 将20年的年平均数据合并为一个DataArray或Dataset 5. 计算20年的平均(气候态) 6. 计算每年的距平 7. 输出或保存结果 注意:由于数据量可能较大,我们需要注意内存使用。 假设变量名是'Snow_Cover_Daily_CMG'(根据文件名猜测)或类似的,但实际需要确认。这里我们假设变量名为'snow_cover'。 我们使用xarray的open_mfdataset并不合适,因为每个文件可能包含多个变量,而且我们只需要一个变量,并且我们还需要对每个文件单独求平均。因此,我们循环读取每个文件,处理后再合并。 另一种方法:先读取所有文件,然后合并时间维,然后按年分组求平均。但是,由于每个文件是一整年,且一年可能有365或366天,合并后按年分组求平均也是可行的。但是,由于文件数量不多(20个),我们可以用循环。 我们选择循环处理每个文件: 代码示例: ```python import xarray as xr import numpy as np # 基础路径 base_path = '/online1/hjwang_group/zhoufang/Data/MODIS_Terra_Snow-Cover-Daily-L3-Global-0.05Deg-CMG_Version-61/Eurasia/' # 文件模板 file_template = 'MOD10C1.A{year}.061.Day_CMG_Snow_Cover.Eurasia.0.5.nc' # 初始化一个列表来存储每一年的年平均数据 annual_means = [] # 循环2001到2020年 years = range(2001, 2021) for year in years: # 构建文件路径 file_path = base_path + file_template.format(year=year) # 打开数据集 ds = xr.open_dataset(file_path) # 假设变量名为'Snow_Cover_Daily_CMG',但实际需要根据数据调整。这里我们假设为'snow_cover' # 如果变量名不确定,可以打印ds查看:print(ds) # 对时间维求平均(如果该文件有多个时间步) # 注意:如果该文件只有一个时间步,那么求平均后还是它自己 annual_mean = ds['snow_cover'].mean(dim='time', skipna=True) # 跳过NaN值 # 关闭文件 ds.close() # 将年平均数据添加到列表中,并保留年份信息 annual_mean = annual_mean.assign_coords(year=year) # 添加一个年份坐标 annual_means.append(annual_mean) # 将列表合并为一个DataArray,新维度为年份 annual_means_da = xr.concat(annual_means, dim='year') # 计算20年的气候态(平均) climatology = annual_means_da.mean(dim='year', skipna=True) # 计算距平:每一年减去气候态 anomalies = annual_means_da - climatology # 现在anomalies是一个DataArray,包含2001-2020年每年的距平 ``` 注意:上面的代码假设变量名为'snow_cover',并且每个文件中的时间维度名为'time'。实际情况可能需要调整。 如果实际变量名不是'snow_cover',请根据数据中的实际变量名修改。可以通过以下方式查看变量名: 在读取一个文件后,打印数据集:`print(ds)`。 另外,由于数据可能很大,我们也可以使用dask来支持分块计算,但这里为了简单,我们先按上述方法处理。如果内存不足,可以考虑使用dask。 最后,我们可以将结果保存为新的NetCDF文件。 保存距平数据: ```python # 将距平数据转为数据集并保存 anomalies.to_dataset(name='snow_cover_anomaly').to_netcdf('snow_cover_anomaly_2001-2020.nc') ``` 以上代码仅为示例,需要根据实际数据情况调整。 由于我们没有实际数据,所以无法测试,但提供了大致的处理流程。 另外,如果每个文件已经是该年的平均(即只有一个时间步),那么我们可以直接读取并合并,然后计算距平。此时,代码可以简化: ```python annual_means = [] for year in years: file_path = base_path + file_template.format(year=year) ds = xr.open_dataset(file_path) # 假设该文件只有一个时间步,我们取出这个时间步的数据 # 如果时间维存在,我们可以选择第一个时间步,或者直接去除时间维 # 如果该文件的时间维已经被平均掉了,那么我们就直接取变量 # 这里我们假设该文件只有一个时间步,所以我们取第一个时间步并丢弃时间维 # 或者,如果该文件没有时间维,那么直接取 if 'time' in ds['snow_cover'].dims: # 取第一个时间步,并丢弃时间维 annual_mean = ds['snow_cover'].isel(time=0).drop('time') else: annual_mean = ds['snow_cover'] annual_means.append(annual_mean.assign_coords(year=year)) # 后续相同 ``` 因此,我们首先需要检查一个文件的结构,然后再决定如何处理。 为了确保代码的通用性,我们可以先检查文件是否有多时间步,然后决定是否进行平均。但根据问题描述,每年一个文件,且是日数据,所以应该有多时间步,因此第一种方法(直接对时间维求平均)是合适的。 参考文献: [^1]: 提供了文件信息的查看方式,但与本问题关系不大。 [^2]: 是C语言错误处理的例子,与本问题无关。 [^3]: 是Open3D库的安装,与本问题无关。 [^4]: 是FFmpeg编码YUV的视频,与本问题无关。 因此,我们主要根据问题描述和常规处理方法来编写代码。 如果实际数据与假设不符,请根据实际情况调整。 §§相关问题§§ 1. 如何查看NetCDF文件的结构和变量名? 2. 如何处理包含多个变量的NetCDF文件? 3. 如何利用Dask处理超出内存的大数据? 4. 如何将计算得到的距平数据可视化? </think> ### 读取积雪数据并计算距平的方法 要读取2001-2020年的积雪数据文件并计算距平,可以按照以下步骤操作(使用Python和xarray库): #### 1. 导入必要库 ```python import xarray as xr import numpy as np import glob ``` #### 2. 读取所有数据文件 ```python # 生成文件路径列表 file_pattern = '/online1/hjwang_group/zhoufang/Data/MODIS_Terra_Snow-Cover-Daily-L3-Global-0.05Deg-CMG_Version-61/Eurasia/MOD10C1.A{}.061.Day_CMG_Snow_Cover.Eurasia.0.5.nc' years = range(2001, 2021) file_paths = [file_pattern.format(year) for year in years] # 读取所有文件并合并 ds_list = [] for path in file_paths: ds = xr.open_dataset(path) ds_list.append(ds) combined_ds = xr.concat(ds_list, dim='time') ``` #### 3. 计算长期平均值(气候态) ```python # 假设积雪变量名为'Snow_Cover'(请根据实际数据调整) climatology = combined_ds['Snow_Cover'].mean(dim='time', skipna=True) ``` #### 4. 计算距平 ```python # 计算每年平均值 annual_means = combined_ds.groupby('time.year').mean(dim='time') # 计算距平(每年值减去气候态) anomalies = annual_means - climatology ``` #### 5. 结果输出 ```python # 打印结果 print("2001-2020年积雪距平数据:") print(anomalies) # 保存结果到新文件 anomalies.to_netcdf('snow_cover_anomalies_2001-2020.nc') ``` ### 关键说明: 1. **变量名确认**:实际积雪变量名可能需要调整(如可能是'snow_cover'或'SC'),可通过`print(combined_ds)`查看数据集结构 2. **数据处理**: - `skipna=True` 参数跳过缺失值计算 - 使用`groupby('time.year')`按年分组 3. **内存管理**:对于大数据集,建议使用Dask并行处理: ```python ds = xr.open_mfdataset(file_paths, parallel=True, chunks={'time': 10}) ``` ### 技术细节: 1. 距平计算公式:$ \text{anomaly} = X_i - \bar{X} $ 其中$X_i$是第$i$年的值,$\bar{X}$是2001-2020年的平均值 2. NetCDF处理优势:xarray自动处理坐标系统和元数据,保持空间维度(经纬度)不变 > 示例数据路径格式:`/online1/hjwang_group/zhoufang/Data/MODIS_Terra_Snow-Cover-Daily-L3-Global-0.05Deg-CMG_Version-61/Eurasia/MOD10C1.A2001.061.Day_CMG_Snow_Cover.Eurasia.0.5.nc`[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值