2022杭电多校六 1006&1010

121 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

2022杭电多校六 1006-Maex (树形DP)

image.png

样例输入:

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

样例输出:

8
6
1

题意:给你一个n个节点的有根树,根节点是1,我们可以给n个节点每个节点一个权值,但是任意两个节点的权值是不能相同的,定义bi为以第i个节点为根的子树中所有节点权值的mex值,的问我们bi和的最大值。

因为每个节点的权值必须两两不同,0只能在某个子树内,所以除了这个子树之外的其他节点所对应的b一定为0,所以我们可以尽可能让这棵子树之内的所有点的权值尽可能连续,那么我们根节点的b值就大。我们可以令f[i]表示以i节点为根的子树中所有节点的mex之和最大值是多少,再用一个cnt[i]记录以i节点为根的子树中所有节点的个数是多少,那么我们直接进行动态转移即可:f[u]=cnt[u]+max(f[v])其中v是u的子节点。

细节见代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=5e6+10;
int e[N],ne[N],h[N],idx;
int cnt[N];
long long f[N];//f[i]记录以i节点为根的子树中所有节点的mex之和的最大值
void add(int x,int y)
{
	e[idx]=y;
	ne[idx]=h[x];
	h[x]=idx++;
}
void dfs(int x,int fa)
{
	cnt[x]=1;
	long long t=0;//记录子节点贡献最大值 
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j==fa) continue;
		dfs(j,x);
		cnt[x]+=cnt[j];
		t=max(f[j],t);
	}
	f[x]=cnt[x]+t;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		idx=0;
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) h[i]=-1; 
		int u,v;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);add(v,u);
		}
		dfs(1,-1);
		printf("%lld\n",f[1]);
	}
	return 0; 
}

2022杭电多校六 1010-Planar graph(最小生成树)

image.png image.png

image.png

题意:给n个点和m条边,m条边将一个图分成若干个连通区域,然后我们可以删除一些边,问至少删除多少条边才能使得所有的区域是连通的,在所有的删边情况中选择字典序最小的一种情况进行输出。

先来思考一下,什么情况下才能使得所有区域是连通的?其实画个图观察不难发现,只有当图中没有环时整个区域才是连通的,现在问题就转化为我们至少删除多少条边才能使得图中没有环,由于树是一个没有环的最大连通子图,所以显然是将图删边使其形成一棵树,这样的话删除的边数一定是最小的,由于题目中要求删除的边的字典序最小,那么我们就可以考虑从编号大的边开始添加边,只要加进去该边不成环就把该边加入图中,这样最后没有加入图中的边就是我们要删除的边,那么我们直接按照字典序进行从小到大输出即可,这个过程就是利用类似kruscal的过程进行实现,只是我们不是按照边权进行排序,而是按照编号进行排序。

下面是代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
const int N=2e6+10;
int fu[N],u[N],v[N];
int find(int x)
{
	if(x!=fu[x]) fu[x]=find(fu[x]);
	return fu[x];
}
stack<int>st;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) fu[i]=i;
		for(int i=1;i<=m;i++)
			scanf("%d%d",&u[i],&v[i]);
		for(int i=m;i>=1;i--)
		{
			int fx=find(u[i]),fy=find(v[i]);
			if(fx==fy)
				st.push(i);
			else 
				fu[fx]=fy;
		}
		printf("%d\n",st.size());
		while(!st.empty())
		{
			printf("%d ",st.top());
			st.pop();
		}
		puts("");
	}
	return 0;
}