[BZOJ2117][2010国家集训队]Crash的旅游计划

本文探讨了一道关于树结构的算法题目,采用分治和树DP等高级数据结构技术来解决从每个节点出发到达其他节点第k大距离的问题。通过细致的分析和优化,最终实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给一棵树,求从每个点开始走向其它n-1个点的距离中第k大值。

又是第k大简直cqoi既视感23333

其实我一开始看到这个第k大就想起了cqoi那个k光滑数,那个题是用持久化可并堆做dp,然后这个题我就想能不能写个主席树合并然后用主席树跑树dp来表示每个点走到其它点的距离什么的,然后发现不可做。。如果用持久化平衡树的话应该是可以的,因为兹瓷打整体加的标记,但是就需要二分答案了。。

然后再想了想,发现就是紫荆花弱化版,然后看了看时限,150秒,就觉得非常靠谱。

考虑二分答案,就转化为查询有多少点到一个点的距离不超过x。跑点分治,每层分治重心维护每个点到它的距离,存到vector中排序,然后由于同一棵子树中的需要减掉,还要保存重心的邻接点的vector。然后拿到一个x值的时候在分治树中往上跳即可。

具体实现的时候还要加个树倍增,因为要求两个点之间的距离,要找树中一个点到另一个点的邻接点是哪一个。

本地造了个测极限数据,大约要20s,感觉有点蛋疼。。然后考虑常数优化。

倍增是肯定要换的。改成链剖+dfs序系列,快了3秒。

还有就是在分治树中往上跑的时候如果答案超过K就直接退出。容易发现越外层的分治块对答案的贡献更大,更有可能提前退出,所以先递归统计外层的分治块,快了差不多5秒。

然后随便再优化点细节就卡进10s了,不管了交上去,本来以为要T飞,结果30s就跑出来了。。还进了前三。。。看来大数据最多也就一两组吧。。


#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define clr(a) memset(a,0,sizeof a)
#define LL long long
using namespace std;
const int MAXN = 100005, inf = 1000000000;
int N, K, rbd;

struct Ed {
	int to, d; Ed*nxt;
} Edges[MAXN*2], *ecnt=Edges, *adj[MAXN];
void adde(int a, int b, int d)
{
	(++ecnt)->to=b;
	ecnt->to = b;
	ecnt->d = d;
	ecnt->nxt=adj[a];
	adj[a] = ecnt;
}

int dep[MAXN], dis[MAXN];
int siz[MAXN], hsn[MAXN], htp[MAXN], fa[MAXN], dfi[MAXN], tmr, id[MAXN];
void dfs1(int u)
{
	siz[u] = 1;
	for (Ed*p=adj[u]; p; p=p->nxt)
		if (p->to!=fa[u])
		{
			dep[p->to]=dep[u]+1, dis[p->to]=dis[u]+p->d;
			fa[p->to] = u, dfs1(p->to), siz[u]+=siz[p->to];
			if (siz[p->to]>siz[hsn[u]]) hsn[u]=p->to;
		}
}
void dfs2(int u)
{
	dfi[u] = ++tmr; id[tmr] = u;
	if (hsn[u]) htp[hsn[u]] = htp[u], dfs2(hsn[u]);
	for (Ed*p=adj[u]; p; p=p->nxt)
		if (p->to!=hsn[u]&&p->to!=fa[u])
			htp[p->to]=p->to, dfs2(p->to);
}
int lca(int u, int v)
{
	while (htp[u]!=htp[v])
	{
		if (dep[htp[u]]>dep[htp[v]]) u=fa[htp[u]];
		else v=fa[htp[v]];
	}
	return dep[u]<dep[v]?u:v;
}
int jump(int u, int d)
{
	while (dep[htp[u]]>d) u = fa[htp[u]];
	return id[dfi[u]-(dep[u]-d)];
}
int Findadj(int c, int u)
{
	if (lca(u, c) == c) return jump(u, dep[c]+1);
	return fa[c];
}
inline int dist(int u, int v)
{
	return dis[u]+dis[v]-2*dis[lca(u,v)];
}

int sz[MAXN], f[MAXN], vn, cg;
bool vis[MAXN];
struct tdc {
	int pre;
	vector<int> vec;
	map<int, vector<int> > adj_vec;
} dc[MAXN];
int getsz(int u, int fa)
{
	int ret = 1;
	for (Ed*p=adj[u];p;p=p->nxt)
		if (!vis[p->to]&&p->to!=fa)
			ret += getsz(p->to, u);
	return ret;
}
void dfscg(int u, int fa)
{
	f[u] = 0, sz[u] = 1;
	for (Ed*p = adj[u]; p; p=p->nxt)
	{
		if (p->to==fa||vis[p->to]) continue;
		dfscg(p->to, u);
		f[u] = max(f[u], sz[p->to]);
		sz[u] += sz[p->to];
	}
	f[u] = max(f[u], vn-sz[u]);
	if (f[u]<f[cg]) cg = u;
}
int Dis[MAXN];
void dfs(int u, int fa, vector<int>&v1, vector<int>&v2)
{
	v1.push_back(Dis[u]), v2.push_back(Dis[u]);
	for (Ed*p=adj[u]; p; p=p->nxt)
		if (p->to!=fa&&!vis[p->to])
			Dis[p->to]=Dis[u]+p->d, dfs(p->to, u, v1, v2);
}
void dac(int u)
{
	vis[u] = 1;
	dc[u].vec.push_back(0);
	for (Ed*p=adj[u]; p; p=p->nxt)
	{
		if (vis[p->to]) continue;
		Dis[p->to] = p->d;
		vector<int> &v0 = dc[u].adj_vec[p->to];
		dfs(p->to, u, dc[u].vec, v0);
		sort(v0.begin(), v0.end());
	}
	sort(dc[u].vec.begin(), dc[u].vec.end());
	for (Ed*p=adj[u]; p; p=p->nxt)
	{
		if (vis[p->to]) continue;
		vn = getsz(p->to, u);
		cg = 0, dfscg(p->to, u);
		dc[cg].pre = u, dac(cg);
	}
}

int GetLess(vector<int>&v, int x)
{
	if (v.empty() || x < *v.begin()) return 0;
	if (x >= *v.rbegin()) return v.size();
	return upper_bound(v.begin(), v.end(), x) - v.begin();
}

int cal(int u, int v, int d)
{
	int tot = 0;
	if (dc[u].pre) tot+=cal(dc[u].pre, v, d);
	if (tot > K) return tot;
	vector<int>&v1 = dc[u].vec;
	int x = dist(u, v);
	tot += GetLess(v1, d-x);
	if (u != v)
	{
		int t = Findadj(u, v);
		tot -= GetLess(dc[u].adj_vec[t], d-x);
	}
	return tot;
}

void solve(int u)
{
	int l = 0, r = rbd, mid, ans = 0;
	while (l <= r)
	{
		mid = (l+r)>>1;
		if (cal(u, u, mid)-1 < K) l = mid + 1;
		else ans = mid, r = mid - 1;
	}
	printf("%d\n", ans);
}

int main()
{
	f[0] = inf;
	scanf("%*s%d%d", &N, &K);
	int u, v, w;
	rep(i, 2, N)
	{
		scanf("%d%d%d", &u, &v, &w);
		adde(u,v,w), adde(v,u,w), rbd += w;
	}
	dfs1(1), htp[1]=1, dfs2(1);
	vn = N, dfscg(1,0), dac(cg);
	rep(i, 1, N) solve(i);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值