题解 [CF1039D] You Are Given a Tree

传送门

莫名……神仙?
首先考虑对于一个固定的 \(k\) 怎么做。
不会做,只会 \(O(nk^2)\) 可以见这个题,用同样的方法可以得到一个 \(O(n)\) 的贪心
那么现在可以做到 \(O(n^2)\)
然后做法就比较显然了,发现取值相同的很多
仔细想想发现不同的取值只有根号种
因为 \(k\leqslant \sqrt n\) 只有根号个 \(k\)
\(k>\sqrt n\) 时取值 \(\leqslant \frac{n}{k}\leqslant \sqrt n\)
那么每次二分一个取值连续段的右端点即可卡过
复杂度 \(O(n\sqrt{n\log n})\),还有 \(O(n\log^2 n)\) 的整体二分做法咕掉了

  • 多次树形 DP 时先预处理出 dfs 序然后在 dfs 序上 DP 以节省递归开销可以加速(大约)一倍多
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int head[N], fir[N], sec[N], mp[N], sta[N], back[N], ecnt, now, ans, sqr, top;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}

void dfs(int u, int fa) {
	sta[++top]=u;
	for (int i=head[u],v; ~i; i=e[i].next) {
		v = e[i].to;
		if (v==fa) continue;
		back[v]=u;
		dfs(v, u);
	}
}

int calc(int k) {
	if (~mp[k]) return mp[k];
	now=k; ans=0;
	for (int i=top; i; --i) {
		int u=sta[i];
		fir[u]=sec[u]=0;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==back[u]) continue;
			if (fir[v]>=fir[u]) sec[u]=fir[u], fir[u]=fir[v];
			else if (fir[v]>sec[u]) sec[u]=fir[v];
		}
		if (fir[u]+sec[u]+1>=now) ++ans, fir[u]=0;
		else ++fir[u];
	}
	return mp[k]=ans;
}

signed main()
{
	n=read();
	memset(mp, -1, sizeof(mp));
	memset(head, -1, sizeof(head));
	for (int i=1,u,v; i<n; ++i) {
		u=read(); v=read();
		add(u, v); add(v, u);
	}
	dfs(1, 0);
	sqr=sqrt(n*log2(n));
	for (int i=1; i<=min(sqr, n); ++i) printf("%d\n", calc(i));
	for (int l=sqr+1,r; l<=n; l=r+1) {
		int tl=l, tr=n, mid, val=calc(l);
		while (tl<=tr) {
			mid=(tl+tr)>>1;
			if (calc(mid)==val) tl=mid+1;
			else tr=mid-1;
		}
		r=tl-1;
		for (int i=l; i<=r; ++i) printf("%d\n", val);
	}
	
	return 0;
}
posted @ 2022-05-08 15:58  Administrator-09  阅读(1)  评论(0编辑  收藏  举报