LCA求带权树上点对的距离 洛谷【P3806】

我是题目传送门呀
题目是点分治的模板题,然而我不会点分治,数据是10000,LCA枚举一下复杂度是(n2logn),掐指一算(python算的 ),复杂度不是刚刚好吗?就想着写一个LCA莽一下,本来以为不会TLE的,然而只有60分。

既然代码没有AC,那我就写一篇博客吧

LCA求点对距离很简单,对于点对(u,v),其距离就是dis[u]+dis[v]-2*dis[lca(u,v)],这个很简单吧,大家很容易想明白的。

dis就是根到结点的距离啦。
然后我dfs直接从1开始,我就默认他是根节点啦,其实哪个点是根节点对于点对的距离是没有影响的。

下面还是放代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int fa[maxn][21];
int lg[maxn];
int depth[maxn];
int vis[maxn];
int dis[maxn];
int n,m;
struct node
{
	int to;
	int cost;
};
vector<node>G[maxn]; 
void dfs(int now,int last)
{
	vis[now] = 1;
	depth[now] = depth[last]+1;
	fa[now][0] = last;
	for(int i=1;(1<<i)<=depth[now];i++)
	{
		fa[now][i] = fa[fa[now][i-1]][i-1];
	}
	for(int i=0;i<G[now].size();i++)
	{
		if(G[now][i].to!=last)
		{
			dis[G[now][i].to] = dis[now]+G[now][i].cost;
			dfs(G[now][i].to,now);
		}
	}
}
int lca(int x,int y)
{
	if(depth[x]>depth[y]) swap(x,y);
	while(depth[x]!=depth[y])
	{
		y = fa[y][lg[depth[y]-depth[x]]-1];
	}
	if(x==y) return x;
	for(int i=lg[depth[x]]-1;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	return fa[x][0];
}
int query(int k)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==j) continue;
			if(dis[i]+dis[j]-2*dis[lca(i,j)]==k) return true;
		}
	}
	return false;
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<n-1;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		G[x].push_back({y,z});
	}
	for(int i=1;i<=n;i++)
	{
		lg[i] = lg[i-1]+(1<<lg[i-1]==i);
	}
	dfs(1,0);
	int k;
	for(int i=0;i<m;i++)
	{
		cin>>k;
		if(query(k)) cout<<"AYE"<<endl;
		else cout<<"NAY"<<endl;
	}
	return 0;
}

第一次用MarkDown,试试啥效果。
为什么代码没高亮啊

然后我点分治也T了四个点,也只有60分qwq
奥奥,点分治写炸了,下面放上100分的点分治吧!

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int n,m;
int sum,rt;
int sz[maxn];
struct node
{
	int to;
	int cost;
};
vector<node> G[maxn];
int mxf[maxn],vis[maxn];
int dis[maxn],ans[10000010];
int cnt;
void getrt(int now,int last)
{
	sz[now] = 1;
	mxf[now] = 0;
	for(int i=0;i<G[now].size();i++)
	{
		int v = G[now][i].to;
		if(v==last || vis[v]) continue;
		getrt(v,now);
		sz[now] += sz[v];
		mxf[now] = max(mxf[now],sz[v]);
	}
	mxf[now] = max(mxf[now],sum-sz[now]);
	if(mxf[now]<mxf[rt]) rt = now;
}
void getdis(int now,int last,int len)
{
	dis[++cnt] = len;
	for(int i=0;i<G[now].size();i++)
	{
		int v = G[now][i].to;
		if(vis[v] || v==last) continue;
		getdis(v,now,len+G[now][i].cost);
	}
}
void solve(int now,int len,int w)
{
	cnt = 0;
	getdis(now,0,len);
	for(int i=1;i<=cnt;i++)
	{
		for(int j=1;j<=cnt;j++)
		{
			if(i!=j) ans[dis[i]+dis[j]] += w;
		}
	}
}
void divide(int x)
{
	solve(x,0,1);
	vis[x] = 1;
	for(int i=0;i<G[x].size();i++)
	{
		int v = G[x][i].to;
		if(vis[v]) continue;
		solve(v,G[x][i].cost,-1);
		sum = sz[x];
		rt = 0;
		mxf[0] = n;
		getrt(v,x);
		divide(rt);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int x,y,z;
	for(int i=0;i<n-1;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		G[x].push_back({y,z});
		G[y].push_back({x,z});
	}
	sum = mxf[0] = n;
	rt = 0;
	getrt(1,0);
	divide(rt);
	int k;
	for(int i=0;i<m;i++)
	{
		scanf("%d",&k);
		if(ans[k]) printf("AYE\n");
		else printf("NAY\n");
	}
	return 0;
}

淀粉质真好吃qwq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值