2671 平衡环(最近公共祖先)

给定一棵n个点的树,每条边有一个边权,边权只可能是4或7。你想在树上选择两个没有直接连边的点,并在这两点间加一条边权为4或
7的连边。很显然,树上出现一个环,如果这个环上边权为4的边数与边权为7的边数相等,那么这个环是一个平衡环。你希望得到这样一个平衡环,请问需要连接哪两个点,并赋予什么边权。
如果有多种方案,输出任意一组解即可。如果无解,输出-1。
数据范围:1=<n<=100
输入
第一行一个整数n,表示树中节点数。
接下来n-1行,每行三个数u, v, w,表示u,v两点间连有一条权值为w的边,w只可能是4或7
输出
输出仅一行,包括三个整数u, v, w,表示你将u,v两节点用权值为w的边连接,形成一个平衡环。
要求u, v在树中不存在连边。
如果无解,输出-1。
输入样例
5
1 2 4
3 2 7
2 4 4
4 5 7
输出样例
1 5 7
设1为根,
求出每点i到根的所有权值为4和7的个数sum[i][4],sum[i][7];
任两点间边权为4的个数为sum[i][4]+sum[j][4]-sum[pre][4];
pre为其最近公共祖先
最近公共祖先的板子在这

#include<bits/stdc++.h>
using namespace std;
const int N=110*2+1;      //序列个数为 2*n-1 
vector<int>tr[N];      //邻接表存图
int vs[N];             //第i次访问节点的编号
int depth[N];          // 第i次访问节点的深度
int id[N];             //在vs数组中i节点第一次出现的下标 
int vis[N];            //标记数组
int st[N][N];         //存最小值对应的下标
int g[N][N],fa[N],sum[N][10];
int n,m,x,y,w;           //图的顶点,查询次数
int dfs_clock=1; 
//计算并存储每个节点出的顺序与层数 
void dfs(int u,int d)  //当前点,它的父节点,点深度 
{
 id[u]=dfs_clock;      //u第一次出现的下标是dfs_clock,从0开始
 vs[dfs_clock]=u;      //标记第dfs_clock访问节点是谁 
 depth[dfs_clock++]=d; //第dfs_clock访问节点的深度是d
 for(auto x:tr[u])
 {
 	if(vis[x])         //访问过的不再访问 
     continue;
    fa[x]=u;
    vis[x]=1;          //访问标记
	dfs(x,d+1);        //往下一层搜索,深度加1 
	vs[dfs_clock]=u;   //回溯节点也要标记
	depth[dfs_clock++]=d; 
 }
}
void RMQ(int nn)//
{
 for(int i=1;i<=nn;i++)
  st[i][0]=i;
 for(int j=1;(1<<j)<=nn;j++)
  for(int i=1;i+(1<<j)-1<=nn;i++)
  {
  	int a=st[i][j-1];
  	int b=st[i+(1<<(j-1))][j-1];
  	if(depth[a]<=depth[b])
  	 st[i][j]=a;
  	else
  	 st[i][j]=b;
  }
}
int LCA(int u, int v)
{
	int l = id[u];
	int r = id[v];
	if(l > r)
		swap(l, r);
	int k = log2(r - l + 1);
	int a = st[l][k];
	int b = st[r - (1 << k) + 1][k];
	return depth[a] > depth[b] ? vs[b] : vs[a];
}
void search(int x)
{
 int t,y=x;
 while(1)
 {
  t=fa[y];
  sum[x][g[y][t]]++;
  y=t;
  if(t==1) 
   return;
 }
}
int main()
{
 ios::sync_with_stdio(false);
 cin>>n;
 for(int i=0;i<n-1;i++)
 {
 	cin>>x>>y>>w;
 	g[x][y]=g[y][x]=w;
 	tr[x].push_back(y);
 	tr[y].push_back(x);
 }
 vis[1]=1;//1标记以查询 
 dfs(1,0);//以1为根 
 RMQ(dfs_clock-1);
 for(int i=1;i<=n;i++)
 {
	if(i==1) continue;
	 search(i);
 }
 for(int i=1;i<=n;i++)
 {
 	for(int j=i+1;j<=n;j++)
 	{
 	 int t=LCA(i,j);
 	 int si=sum[i][4]+sum[j][4]-sum[t][4];
	 int qi=sum[i][7]+sum[j][7]-sum[t][7];
	 if(g[i][j]==0&&(si+1==qi||qi+1==si))//g[i][j]为0代表两点不相连
	 {
	  cout<<i<<" "<<j<<" "<<(si<qi?4:7);return 0;	
	 }	
	}
 }
 cout<<-1;
 return 0; 
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值