A tree is an undirected graph with exactly one simple path between each pair of vertices. We call a set of simple paths kk-valid if each vertex of the tree belongs to no more than one of these paths (including endpoints) and each path consists of exactly kk vertices.
You are given a tree with nn vertices. For each kk from 11 to nn inclusive find what is the maximum possible size of a kk-valid set of simple paths.
The first line of the input contains a single integer nn (2≤n≤1000002≤n≤100000) — the number of vertices in the tree.
Then following n−1n−1 lines describe the tree, each of them contains two integers vv, uu (1≤v,u≤n1≤v,u≤n) — endpoints of the corresponding edge.
It is guaranteed, that the given graph is a tree.
Output nn numbers, the ii-th of which is the maximum possible number of paths in an ii-valid set of paths.
7 1 2 2 3 3 4 4 5 5 6 6 7
7 3 2 1 1 1 1
6 1 2 2 3 2 4 1 5 5 6
6 2 2 1 1 0
One way to achieve the optimal number of paths for the second sample is illustrated in the following picture:
题意:给你一棵树,问用长度为k的链去覆盖它,链两两不能相交,最多能盖多少条链上去。
我们从简单的情况考虑,在一条链上对于每一个k答案会是n/k。并且我们注意到n/k的整出性质:只有sqrt(n)种取值,并且答案单调。我们大胆猜想,树上情况的取值也在sqrt(n)级别内,显然也是单调的。
如果我们可以对于k单点求值那么我们就可以记忆化+二分在sqrt(n)log(n)的额外复杂度下解决本题,那么问题在于如何对于k单点求值。
考虑贪心,每一次我们枚举最高点在u的链的放置方案,这样链的两端一定插在不同的儿子中。我们发现每一个儿子对父亲的贡献只有向下到达的最深点,因为任意两条链不能重叠。那么每一次找寻儿子中最深的两个,如果大于k那么就放置,否则更新当前u的最深处。总复杂度:O(n sqrt(n)log(n))
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5 + 5;
struct edge {
int v, next;
} e[maxn << 1];
int head[maxn], cnt, depth[maxn], dfn[maxn], dcnt, ans, fa[maxn];
int n, u, v, mem[maxn], _1st[maxn], _2nd[maxn], ord[maxn], B = 500;
void adde(const int &u, const int &v) {
e[++cnt] = (edge) {v, head[u]};
head[u] = cnt;
}
void dfs(const int &u, const int &p) {
fa[u] = p;
dfn[u] = ++dcnt;
ord[dcnt] = u;
for(register int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if(v == p) continue;
dfs(v, u);
}
}
int Calc(const int &k) {
int p, u, ans = 0;
memset(_1st, 0, sizeof(_1st));
memset(_2nd, 0, sizeof(_2nd));
for(register int i = n; i >= 1; --i) {
u = ord[i], p = fa[u];
if(!_1st[u]) depth[u] = 1;
if(_2nd[u] + _1st[u] + 1 >= k) ++ans, depth[u] = 0;
else depth[u] = _1st[u] + 1;
if(depth[u] > _1st[p]) _2nd[p] = _1st[p], _1st[p] = depth[u];
else if(depth[u] > _2nd[p]) _2nd[p] = depth[u];
}
return ans;
}
int DF(int l, int r, int ans) {
while(l < r - 1) {
int mid = l + r >> 1;
if(mem[mid] == -1)
mem[mid] = Calc(mid);
if(mem[mid] >= ans) l = mid;
else r = mid;
}
return l;
}
int main() {
memset(mem, -1, sizeof(mem));
scanf("%d", &n);
for(register int i = 1; i < n; ++i)
scanf("%d%d", &u, &v), adde(u, v), adde(v, u);
dfs(1, 0), mem[1] = n, B = min(B, n);
for(register int i = 2; i <= B; ++i)
mem[i] = Calc(i);
for(register int l = B + 1, r; l <= n; l = r + 1) {
if(mem[l] == -1) mem[l] = Calc(l);
r = DF(l, n + 1, mem[l]);
for(register int i = l + 1; i <= r; ++i)
mem[i] = mem[l];
}
for(register int i = 1; i <= n; ++i)
printf("%d\n", mem[i]);
return 0;
}
rockdu
没有帐号? 立即注册