首页 > 其他分享 >P10678 『STA - R6』月 题解

P10678 『STA - R6』月 题解

时间:2024-10-06 09:11:14浏览次数:1  
标签:度数 R6 题解 sum cry P10678 &&& ord 节点

Solution

看了别的大佬的题解,感觉都是数学证明然后用树和图做的,看不懂啊。。。萌新瑟瑟发抖

用 vector 模拟树,然后贪心摸索做出来了。注意到要求最深叶子结点和最浅叶子结点的距离最短时的情况,那么此时根节点应该是树中度数最大的点,把树尽可能的拓宽,深度换宽度。

那么同理的根节点的子树的根节点应该也是该子树中度数最深的点,那么可得出该树应该是类似一个大根堆(出度不一定为 \(2\),因此类似),且每层的数量其实是固定的(由上一层的度数之和决定,而根节点又确定),因此仅通过一次排序,便可得到由根节点到最后一个叶子结点,每层的顺序:

如:样例第四

7
1 3 2 3 1 1 1

通过结构体将每个元素的度数和原次序绑定,得如下顺序:
(后期要改度数用出度,将除了根节点之外的度数均减一)

//表格一
4
2
1 1

&&&
1 2   //原次序
1 1   //度数
&&&

3
1 1 2

&&&
3 1 2
2 1 1
&&&

5
1 1 2 2 2

&&&
3 4 5 1 2
2 2 2 1 1
&&&

7
1 3 2 3 1 1 1

&&&
2 4 3 1 5 6 7
3 3 2 1 1 1 1
&&&

然后再将 "&&" 中的结构体装进 vector 里面模拟树(仅是因为 vector 方便打,其实用树结构体存效果一样):

//表格二
4
2
1 1

&&&1 &&&  //树中存的是节点的次序
&&&2 &&&

&&&1 &&&  //树中存的是节点的出度
&&&0 &&&
3
1 1 2
&&&3 &&&
&&&1 2 &&&

&&&2 &&&
&&&0 0 &&&
5
1 1 2 2 2
&&&3 &&&
&&&4 5 &&&
&&&1 2 &&&

&&&2 &&&
&&&1 1 &&&
&&&0 0 &&&
7
1 3 2 3 1 1 1
&&&2 &&&
&&&4 3 1 &&&
&&&5 6 7 &&&

&&&3 &&&
&&&2 1 0 &&&
&&&0 0 0 &&&

“&”“&” 中的是 vector 数组中每层的情况,(除了根节点之外的节点度数减了 \(1\),转变成出度)。而决定该层数是由上一层的所有点的出度之和(第二层根据根节点的出度)。下一层中的任何一个都可做上一层节点的孩子,不影响树的最深最浅叶子结点层数。


最后把树的情况输出来即可:

如最后一例:

&&&2 &&&         //节点次序
&&&4 3 1 &&&
&&&5 6 7 &&&

&&&3 &&&         //3->2,3->1,3->0
&&&2 1 0 &&&     //2->0,2->0,2->0
&&&0 0 0 &&&     //父节点领养子节点情况

//将上面的诸如 \(2\),\(0\) 之类转成\(↑\)上面树中存的节点次序输出来即可变成:

树

Code

#include <bits/stdc++.h>
using namespace std;
#define to(x,y); for(int x=1;x<=y;x++)
#define fr(x,y); for(int x=0;x<y;x++)
const int N = 2e5 + 20;
int t, n, sum[N];

struct nd {
	int ord, cry;
} p[N];
vector<nd> tr[N];

bool cmp(nd a, nd b) {
	if (a.cry == b.cry)
		return a.ord < b.ord;
	return a.cry > b.cry;
}

int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		to(i, n)scanf("%d", &p[i].cry), p[i].ord = i;
		sort(p + 1, p + 1 + n, cmp);
		to(i, n) {
			if (i > 1)
				p[i].cry--;
			sum[i] = sum[i - 1] + p[i].cry;
		}
		int l = 1, r = 1, num, dep = 1;
		tr[1].push_back({p[1].ord, p[1].cry});
		while (r < n) {
			dep++;
			num = sum[r] - sum[l - 1];
			l = r + 1, r = r + num;
			for (int i = l; i <= r; i++)
				tr[dep].push_back({p[i].ord, p[i].cry});
		}
		to(i, dep - 1) {
			int len = tr[i].size(), idx = 0;
			fr(j, len) {
				for (int k = 0; k < tr[i][j].cry; k++) {
					printf("%d %d\n", tr[i][j].ord, tr[i + 1][idx + k]);
				}
				idx += tr[i][j].cry;
			}
		}
		to(i, dep)vector <nd>().swap(tr[i]);
	}

	return 0;
}

附带表格可视化并附解释

#include <bits/stdc++.h>
using namespace std;
#define to(x,y); for(int x=1;x<=y;x++) //丑陋的代码化简习惯
#define fr(x,y); for(int x=0;x<y;x++)
const int N = 2e5 + 20;
int t, n, sum[N];

// sum 为出度前缀和,求每层的节点数
struct nd {
	int ord, cry;
} p[N];          //存输入数据并做预处理用
vector<nd> tr[N];//核心 vector 树

bool cmp(nd a, nd b) {
	if (a.cry == b.cry)
		return a.ord < b.ord;
	return a.cry > b.cry;
}//排序预处理

int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
//		memset(p, 0, sizeof p); //无用的初始化
		to(i, n)scanf("%d", &p[i].cry), p[i].ord = i;
		sort(p + 1, p + 1 + n, cmp);
		/*表格一*/
//		puts("\n&&&");
//		to(i, n)printf("%d ", p[i].ord);
//		puts("");
//		to(i, n)printf("%d ", p[i].cry);
//		puts("\n&&&\n");
		to(i, n) {
			if (i > 1)
				p[i].cry--;//改度数为出度
			sum[i] = sum[i - 1] + p[i].cry;//前缀和
		}
		int l = 1, r = 1, num, dep = 1;
		tr[1].push_back({p[1].ord, p[1].cry});//存下根节点
		while (r < n) {
			dep++;//由上一层来到下一层
			num = sum[r] - sum[l - 1];//上层度数之和
			l = r + 1, r = r + num;
			for (int i = l; i <= r; i++)//“领养”子节点
				tr[dep].push_back({p[i].ord, p[i].cry});//存下该层子节点
		}
		/*表格二*/
//		to(i, dep) {
//			printf("&&&");
//			int len = tr[i].size();
//			fr(j, len)printf("%d ", tr[i][j].ord);
//			puts("&&&");
//		}
//		puts("");
//		to(i, dep) {
//			printf("&&&");
//			int len = tr[i].size();
//			fr(j, len)printf("%d ", tr[i][j].cry);
//			puts("&&&");
//		}
		to(i, dep - 1) {
			int len = tr[i].size(), idx = 0;
			// idx 为领养子节点的次序
			fr(j, len) {
				for (int k = 0; k < tr[i][j].cry; k++) {
					printf("%d %d\n", tr[i][j].ord, tr[i + 1][idx + k]);
				}//输出父子
				idx += tr[i][j].cry;//领下一批子节点
			}
		}
		to(i, dep)vector <nd>().swap(tr[i]);
		//清空 vector 树
	}
	//完结撒花
	return 0;
}

标签:度数,R6,题解,sum,cry,P10678,&&&,ord,节点
From: https://www.cnblogs.com/badn/p/18448852

相关文章

  • 题解:P11008 『STA - R7』异或生成序列
    Solution序列\(p\)是\(1\)~\(n\)的排列,因此考虑搜索回溯。由\(\sumn\le2\times10^6\)得知\(O(n^2)\)会炸,深感遗憾但仍考虑剪枝。坚信深搜过百万的蒟蒻。。。原\(b\)序列为长度\(n-1\)的序列:{\(b_1,b_2,b_3\cdotsb_n-1\)}将其前面插入一个元素\(......
  • 题解:AT_abc374_d [ABC374D] Laser Marking
    看到\(n\le6\),就知道这道题又是一道搜索了。题面有点长,信息也给的有点多,但稍微分析就可以得到只需要搜索印刷线段的顺序即可。具体的,我们在深搜函数里面传\(4\)个参数,分别代表已选线段的数量,当前位置的横纵坐标,以及当前时间。为了方便,我们表示的是已经印刷完当前线段后的时间......
  • P5593 题解
    题目分析首先考虑什么样的颜色能被链覆盖。容易想到当某种颜色恰巧在一条链上会被覆盖。所以只需要判断一种颜色是否能构成链即可,链的贡献也很好计算。算法考虑链的性质:有且仅有两个端点。凭借这个性质,可以判断一种颜色是否在一条链上。在dfs中考虑各种情况。假设一个......
  • P10418 [蓝桥杯 2023 国 A] 相连的边 题解
    一个比较有趣的树形DP,情况比较多。【题目简述】给定一棵树,求三条相连的边,其边权之和最大。【思路】以X代表当前节点,S表示儿子,G表示孙子,P表示父节点。首先把树建出来,在以下图中,我们模拟二号点的DP过程,考虑以下几种情况:有一条边指向父节点时FG(FatherGrandson):一......
  • 数列 题解
    题意给出一张联通图,图上每个点有红蓝两色,给每一条边都赋一个权值,使得所有的红边构成一颗最小生成树,且权值是\(1\)到\(m\)的排列,求字典序最小的情况。题解对于一条蓝边,其权值一定小于除它以外全是红边的环上的所有红边的权值(有点绕),否则就构不成一颗生成树。所以只需要按顺......
  • CF946G Almost Increasing Array 题解
    题目传送门前置知识最长不下降子序列|权值树状数组及应用解法若将\(\{a\}\)变成严格递增序列,至少需要更改\(n\)减去\(\{a_{i}-i\}\)的最长不下降子序列长度个数。证明对于\(a_{i},a_{j}(i<j)\)若都在最终的严格递增序列里,则有\(a_{i}-a_{j}\lei-j\),即\(......
  • PHP报错getimagesize(): SSL operation failed with code 1问题解决方案
    这个PHP错误通常发生在尝试通过HTTPS协议获取图像时,由于缺少或过期的CA证书导致SSL连接验证失败。以下是详细的解决方案:解决方案一:更新CA证书下载最新的CA证书访问 curl官方提供的CA证书 页面下载 cacert.pem 文件。上传证书文件将下载的 cacert.......
  • U486684 「INOI2016」Brackets 题解
    首先对于回文&括号有一个经典转移:枚举分割点+区间两个端点讨论此题也是如此首先枚举分割点十分套路,如下\[dp_{i,j}=\max_{k=i}^{j-1}dp_{i,k}+dp_{k+1,j}\]如果两个端点相同\[dp_{i,j}=dp_{i+1,j-1}+v_i+v_j\]还有一个转移对于一个区间,因为是子序列,所以一个区间的子区间......
  • qoj9230 Routing K-Codes 题解
    首先这个图肯定不能有环,也不能有度数大于\(3\)的点。也就是说这是一颗二叉树。我们假设父亲都比儿子小,根节点的值最小。那么假设\(u\)点的值为\(x\),它的儿子的值一定是\(\{2x,2x+1\}\)的子集。会发现\(u\)的子树内的权值和是一个关于\(x\)的一次函数。而且无论两个儿......
  • CF1648F Two Avenues 题解
    非常好题目,使我代码旋转。思路考虑什么样的边有贡献。我们首先提出原图的一个dfs树。处理出经过关键点的树上路径在每一条树边的经过次数\(v_i\)。我们选点会有以下几种情况。选两条割边\(i,j\),由于割边肯定是树边,所以答案就是\(v_i+v_j\)。选一条只被一条非树边覆盖......