8.5 杭电杯多校联赛第六场

这篇博客深入探讨了树的遍历算法,包括前序遍历、中序遍历和后序遍历,并通过代码实现展示了如何找到树中节点的MEX值最大和。此外,还介绍了如何使用优先队列和单调栈解决数组的字典序最大排列问题。最后,通过向量运算和不动点定理求解了地图中不动点的坐标。
摘要由CSDN通过智能技术生成

1006 MEX

题意

见代码

思路

见代码

代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=5e5+5,M=2*N;

// n个结点(1-n)的树 再给定n-1条边  1号结点为根结点
// 每个结点有一个权值但未知 并且互不相同 定义每个结点的bi值为 MEX{ 以结点i为根的子树的所有点的权值的集合 }
// MEX{A} 求的是 不在集合A中的最小自然数
// 问 所有结点的bi值的和 最大为多少

// 求解思路:可以推断 要使得总的bi的和最大 某根节点的子树应该填充好0-n-2的权值 然后根节点填n-1 这样根节点bi一定是最大
// 这时根节点的bi 就为n 其实也是以它为根的子树的结点数量 
// 然后因为权值互不相同 从0开始向上填权值只能在一颗子树内做到 此时其它子树结点的bi都是0 所以可以求得
// sum[root] = sz[root] + max(sum[某子结点])  sum[i]表示以i为根的树的所有结点bi值的最大和 sz[i]表示以i为根的子树结点数量
int h[N], e[M], ne[M], idx;
int sz[N];
long long sum[N];

void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

// 注意这种用图存储的树的遍历 一般要带一个父节点进行 
int dfs(int u, int fa) //遍历以fa为父节点的 u结点子树的结点数量
{
	sz[u] = 1;
	for(int i = h[u]; i!=-1; i=ne[i]){
		int j = e[i];
		if(j == fa) continue; // 注意不要回到父节点
		sz[u] += dfs(j, u);  // 加上子树的结点数量
	}
	return sz[u];
}

int dp(int u, int fa) //求以u为根节点的子树 (fa是u的父节点) 所有结点的mex和最大值
{
	sum[u] = 1; //初始化为1
	for(int i = h[u]; i!=-1; i=ne[i]){
		int j = e[i];
		if(j == fa) continue; //注意不要回到父节点
		dp(j, u);
		sum[u] = max(sum[u], sz[u] + sum[j]);   //将子树填0-n-2 自己就是n-1 那么自己的bi值就是n 其实就是子树结点数量 
		// 然后加上某颗子树的bi值的和就是总的bi值 为什么是某一棵子树呢 因为要想bi值和最大  必须一颗一颗子树按照顺序填满
		// 这样可以发现 当某一课子树是从0开始填上去的  其它子树的bi值都是0
		// 所以某个子树最大bi值之和就是 sum[root] = sz[root] + max(sum[某子结点])
	}
	return sum[u];
}

int main()
{
	int t;
	scanf("%d", &t);
	while(t--){
		idx = 0;
		int n;
		scanf("%d", &n);
		memset(h, -1, sizeof(h));
		for(int i = 1; i<n; i++){
			int a, b;
			scanf("%d%d", &a,&b);
			add(a, b), add(b, a);
		}
	
		dfs(1, 0); // 先求sz数组
		long long res = dp(1, 0);
		printf("%lld\n", res);
	}
	
    return 0;
}

1012 Loop

题意

给定n个数,给定k次操作,每次将一个数提出来插到后面,问k次操作之后,数组按照字典序最大排列是什么?

思路

遇到什么情况将k提出来会使得排列字典序变大? 那就是ai < ai+1 的情况,这时我提出来ai,ai+1往前移动,那么由于ai+1>ai,排列的字典序一定会变大,所以我们只要保证将k个这种情况找到,提出来这些数,然后再由大到小考虑插在后面哪里,每一个数插在哪里可以用归并。

代码

#include<bits/stdc++.h>
using namespace std;

const int M=3e5+9;

int a[M];
int aaa[M];
int bbb[M];

void work(){
	int n, k;
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;++i) scanf("%d",&a[i]);
		
	priority_queue<int>aa;
	stack<int>ss;
	
	for(int i = 0; i<n; i++){ 
		while(ss.size() && ss.top() < a[i] && k){ // 维护一个单调栈
			aa.push(ss.top());
			ss.pop();
			k--;
		}
		ss.push(a[i]);
	}

	// 上面维护完之后 aa里面就是要提出来的k个数
	// ss是除了这k个数以外的原数组其它数的逆序
	
	int i;    // 将两个容器转存到普通数组
	int na = ss.size();
	for(i = na-1 ; i>=0; i--){ //注意栈是逆序过来才是原数组顺序
		aaa[i] = ss.top();
		ss.pop();
	}
	
	int nb = aa.size();
	for(i = 0 ; i<nb; i++){
		bbb[i] = aa.top();
		aa.pop();
	}
	
	int ia,ib;  // 归并
	int cnt = 1;
	for(ia = 0, ib = 0; ia<na && ib<nb; ){
		if(aaa[ia] >= bbb[ib]){
			if(cnt++ == n) cout<<aaa[ia++];
			else cout<<aaa[ia++]<<" ";
		} 
		if(aaa[ia] < bbb[ib]){
			if(cnt++ == n) cout<<bbb[ib++];
			else cout<<bbb[ib++]<<" ";
		}
	}
	
	while(ia < na) {
		if(cnt++ == n) cout<<aaa[ia++];
		else cout<<aaa[ia++]<<" ";
	} 
	while(ib < nb) {
		if(cnt++ == n) cout<<bbb[ib++];
		else cout<<bbb[ib++]<<" ";
	}
	
	cout<<endl;
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--) work();
	return 0;
}

/*
2
7 3
1 4 2 1 4 2 4
5 2
4 3 5 4 5
*/

1009 不动点

题意

小地图abcd是大地图ABCD按照原尺寸缩小而来,然后将它放在大地图里面,一定有一个点,它既是大地图这个位置的点,又是小地图这个位置上的点, 求这个点。

思路

不动点定理,用向量求解,设出P点坐标,然后将OP = OA + AP = OA + p*AB + q*AD = oa + aP = oa + p * ab + q * ad。 用x和y两个分量 ,可以列两个方程,然后求解p q,就可以得到P坐标。

代码

#include<cstdio>
#include<iostream>
#include<cmath>

using namespace std;


long double dis(double x1, double y1, double x2, double y2, double x0, double y0)
{
	long double d = (fabs((y2 - y1) * x0 +(x1 - x2) * y0 + ((x2 * y1) -(x1 * y2)))) / (sqrt(pow(y2 - y1, 2) + pow(x1 - x2, 2)));
	return d;
}

int main()
{
	int t;
	scanf("%d", &t);
	
	double Ax, Bx, Cx, Dx;
	double Ay, By, Cy, Dy;

	double ax, bx, cx, dx;
	double ay, by, cy, dy;
	
	while(t--){
		scanf("%lf%lf", &Ax, &Ay);
		scanf("%lf%lf", &Bx, &By);
		scanf("%lf%lf", &Cx, &Cy);
		scanf("%lf%lf", &Dx, &Dy);
		
		scanf("%lf%lf", &ax, &ay);
		scanf("%lf%lf", &bx, &by);
		scanf("%lf%lf", &cx, &cy);
		scanf("%lf%lf", &dx, &dy);
		
		double ABx = Ax - Bx, ABy = Ay - By;
		double abx = ax - bx, aby = ay - by;
		double X1 = ABx - abx, Y1 = ABy - aby;
		
		double ADx = Ax - Dx, ADy = Ay - Dy;
		double adx = ax - dx, ady = ay - dy;
		double X2 = ADx - adx, Y2 = ADy - ady;
		

		double X3 = ax - Ax, Y3 = ay - Ay;
		// pX1 + qX2 = X3
		// pY1 + qY2 = Y3     * (X1/Y1)
		
		double q = (X1*Y3 - X3*Y1) / (Y2*X1 - X2*Y1);
		double p = (X3 - q*X2) / X1;
		double PX = Ax + p*ABx + q*ADx;
		double PY = Ay + p*ABy + q*ADy;
		printf("%.6lf %.6lf\n", PX, PY);
		
	}
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GTTwelve

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值