• CF638C 【Road Improvement】


    题目传送门


    这是一道贪心题。

    • 求出 (k) 其实很简单,因为每个节点的边每天最多只能修一条,所以答案就是度数最大的节点的度数(也就是边最多的节点的边数)。
    • 重点在于怎么求方案。
      1. 因为题目中的树是一颗无根树,所以我们默认 (1) 号节点为他的根节点。
      2. 接下来我们开 (k) 个集合,表示第 (i) 天要修的路的编号(这里可以用 vector 实现)。
      3. 划重点 然后开始从 (1) 号节点 dfs ,每走到一个节点,就把 它与它父亲之间的路它与所有儿子间的路 放在不同的集合中,因为这些边的修筑 必须都有该节点参与 ,但是由于该节点 一天只能修一条路 ,所以要放在不同天修。
      4. 就这样遍历完整棵树,然后按要求输出就可以了。

    Code:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<bitset>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    
    using namespace std;
    
    int read()
    {
    	int ans=0,f=1;
    	char c=getchar();
    	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    	return ans*f;
    }
    
    const int N=2e5+5;
    int n,du[N],head[N],tot,ans;
    //du 用来统计节点度数 
    
    vector<int> agg[N];
    //用 vector 模拟集合 
    
    struct Edge
    {
    	int v,ne;
    }e[N*2];
    //这里要开双倍空间,因为是双向边 
    
    inline void add(int u,int v);
    //加边函数 
    inline void dfs(int u,int fa,int last);
    //dfs 求方案 
    
    int main()
    {
    	n=read();
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();
    		du[u]++,du[v]++;//统计度数 
    		add(u,v);
    		add(v,u);
    		ans=max(ans,max(du[u],du[v]));
    		//取度数最大值为答案 
    	}
    	printf("%d
    ",ans);
    	//先输出答案 
    	dfs(1,-1,0);
    	//然后 dfs 求方案 
    	for(int i=1;i<=ans;++i)
    	{
    		printf("%d ",agg[i].size());
    		//先输出这一天修的路的数量 
    		for(int j=0;j<agg[i].size();++j)
    			printf("%d ",agg[i][j]);   //然后输出具体的路的编号 
    		printf("
    ");
    	}
    	return 0;
    }
    
    inline void add(int u,int v)
    {
    	e[++tot]=(Edge){v,head[u]};
    	head[u]=tot;
    }
    
    //u 表示当前节点,fa 表示 u 节点的父亲,last 表示 u 与 fa 间的路所在的集合编号 
    inline void dfs(int u,int fa,int last)
    {
    	int j=1; // j 表示当前枚举到的集合数 
    	for(int i=head[u];i;i=e[i].ne)
    	{
    		int v=e[i].v;
    		if(v==fa) //判断一下,别走回头路 
    			continue;
    		if(j==last)  //如果当前集合编号是 u 与它父亲间的路所在集合的编号 
    			++j;  //那么这个集合不能放这条边,及 ++j; 
    		agg[j].push_back((i+1)/2);
    		//然后记录答案
    		//因为上面存的是双向边,所以不能直接存 i
    		//考虑到同一条边在数组中的下表是 x,x+1
    		//所以通过 (x+1)/2 可以计算出该边在读入时的编号。 
    		dfs(v,u,j);
    		//继续递归该节点 
    		++j;
    		//因为该集合已经放了边,所以 j 要 +1 
    	}
    	return;
    }
    
  • 相关阅读:
    linux软件相关基操--基于Debian
    Spring AOP实现接口调用异常时重试
    Kafka
    zookeeper集群
    zookeeper客户端之curator
    zk权限模块
    zookeeper简介及基操
    CustomTool
    SpringBoot+Mybatis配置多数据源,分包方式
    mysql操作相关错误解决办法
  • 原文地址:https://www.cnblogs.com/blackbird137/p/13550353.html
Copyright © 2020-2023  润新知