【CodeForces - 1059E】Split the Tree

版权声明:本文为博主原创文章……懂吗?要尊重别人的劳动成果呐 https://blog.csdn.net/Tiw_Air_Op1721/article/details/82953532


@题目描述 - English@

time limit per test:2 seconds
memory limit per test:256 megabytes

You are given a rooted tree on n vertices, its root is the vertex number 1. The i-th vertex contains a number wi. Split it into the minimum possible number of vertical paths in such a way that each path contains no more than L vertices and the sum of integers wi on each path does not exceed S. Each vertex should belong to exactly one path.
A vertical path is a sequence of vertices v1,v2,…,vk where vi (i≥2) is the parent of vi−1.

Input
The first line contains three integers n, L, S (1≤n≤105,1≤L≤105, 1≤S≤1018) — the number of vertices, the maximum number of vertices in one path and the maximum sum in one path.
The second line contains n integers w1,w2,…,wn (1≤wi≤109) — the numbers in the vertices of the tree.
The third line contains n−1 integers p2,…,pn (1≤pi<i), where pi is the parent of the i-th vertex in the tree.

Output
Output one number — the minimum number of vertical paths. If it is impossible to split the tree, output −1.

Examples
input
3 1 3
1 2 3
1 1
output
3

input
3 3 6
1 2 3
1 1
output
2

input
1 1 10000
10001
output
-1

Note
In the first sample the tree is split into {1}, {2}, {3}.
In the second sample the tree is split into {1, 2}, {3} or {1, 3}, {2}.
In the third sample it is impossible to split the tree.

@题目翻译@

将一棵n个点的带权有根树剖分成尽量少的链,使得(1)链的两个端点是祖先关系(2)链含有的顶点个数小于等于L(3)链上所有点的点权和小于等于S。
求出最少链的数量,如果无解输出-1。N<=105

@分析@

【当时比赛的时候我真的想到了正解来着……】
【只是自己带了一个“树上倍增永远写不对”的buff……所以没有A掉QAQ。】
【后来就去调D题。然后D题平方差公式推错……整场比赛就崩掉了QAQ……】

首先,如果所有的链都只包含一个结点,那么如果存在结点i使得它的点权wi>S则无解,否则就有解。

我们令 a[i] 表示将 i 这颗子树按题意剖分得到的最小链条数,再令 s u m [ i ] = ( j j i a [ j ] ) sum[i] = (\sum_j^{j是i的孩子}a[j]) 。不难发现 a[i] 有上界: a [ i ] &lt; = s u m [ i ] + 1 a[i]&lt;=sum[i]+1 。上式表示将 i 单独剖分成一条链的链数。再进一步思考,a[i] 也有下界: s u m [ i ] &lt; = a [ i ] sum[i]&lt;=a[i] 。上式表示增加一个结点过后,链的数量不会变少。

于是: a [ i ] = s u m [ i ] s u m [ i ] + 1 a[i]=sum[i]或sum[i]+1
【为简化描述,如果 a [ i ] = s u m [ i ] a[i]=sum[i] 我们就称 i 为黑点,否则为灰点。】

如果结点 i 是黑点,则它不能够单独成链,那么它肯定与它的某一个孩子共链。所以有这样一个结论:最优策略下,链底端的结点 d 一定满足灰点。

再一步推导,就能得到一个关键的结论:在以结点 i 为根的子树中,如果存在灰点 d ,使得 i~d 是一条合法的链,则 i 为黑点。这个结论可以用归纳法证明(当然也可以猜测它是对的,跳过证明)。

然后,根据上面那个结论可知,灰点一定是在链的底端。所以有链的数量 = 灰点数量。

那么,现在开始描述算法:从根节点开始dfs。将所有的儿子都遍历完后,如果当前点 rt 为灰点,则沿着 rt 往上走,直到走到 p 使得 p ~ rt这条链不合法。然后 p ~ rt上的(除p,rt以外)点记为黑点;否则如果 rt 为黑点,不作任何操作。

找结点 p 可以用倍增法。标记 p~rt 上的点可以用树链剖分。

@代码@

啊啊啊……总觉得还是很可惜啊QAQ
考场上写的代码略丑,请大家见谅。如果大家还有什么疑问或者我的程序错了,可以评论在下面。
我会尽心回复大家的评论的qwq!

#include<cstdio>
typedef long long ll;
const int MAXN = 100000;
const ll INF = (1LL<<60);
struct edge{
	int to;
	edge *nxt;
}edges[MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
	edge *p=(++ecnt);
	p->to = v, p->nxt = adj[u], adj[u] = p;
}
int n, L; ll w[MAXN + 5], S;
int fa[MAXN + 5], dep[MAXN + 5], siz[MAXN + 5];
int fir[MAXN + 5], dfn[MAXN + 5], top[MAXN + 5], dcnt = 0;
void dfs1(int rt, int pre) {
	fa[rt] = pre, dep[rt] = dep[pre] + 1, siz[rt] = 1;
	for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
		if( p->to == pre ) continue;
		dfs1(p->to, rt);
		siz[rt] += siz[p->to];
	}
}
void dfs2(int rt, int tp) {
	top[rt] = tp, fir[rt] = (++dcnt), dfn[dcnt] = rt;
	int hvy = 0;
	for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
		if( p->to == fa[rt] ) continue;
		if( hvy == 0 || siz[p->to] > siz[hvy] )
			hvy = p->to;
	}
	if( !hvy ) return ;
	dfs2(hvy, tp);
	for(edge *p=adj[rt];p!=NULL;p=p->nxt)
		if( p->to != fa[rt] && p->to != hvy )
			dfs2(p->to, p->to);
}
struct node{
	int le, ri;
	bool tag;
}tree[4*MAXN + 5];
void Build(int x, int l, int r) {
	tree[x].le = l, tree[x].ri = r;
	if( l == r ) return ;
	int Mid = (l + r) >> 1;
	Build(x<<1, l, Mid);
	Build(x<<1|1, Mid+1, r);
}
void Modify(int x, int l, int r) {
	if( l > tree[x].ri || r < tree[x].le )
		return ;
	if( l <= tree[x].le && tree[x].ri <= r ) {
		tree[x].tag = true;
		return ;
	}
	if( tree[x].tag ) return ;
	Modify(x<<1, l, r);
	Modify(x<<1|1, l, r);
}
bool Query(int x, int pos) {
	if( pos > tree[x].ri || pos < tree[x].le )
		return false;
	if( pos == tree[x].le && pos == tree[x].ri ) return tree[x].tag;
	if( tree[x].tag ) return true;
	else return Query(x<<1, pos) || Query(x<<1|1, pos);
}
void TModify(int u, int v) {
	while( top[u] != top[v] ) {
		if( dep[top[u]] > dep[top[v]] ) {
			Modify(1, fir[top[u]], fir[u]);
			u = fa[top[u]];
		}
		else {
			Modify(1, fir[top[v]], fir[v]);
			v = fa[top[v]];
		}
	}
	if( dep[u] > dep[v] )
		Modify(1, fir[v], fir[u]);
	else Modify(1, fir[u], fir[v]);
}
int fa1[MAXN + 5][25];
ll sum[MAXN + 5][25];
void dfs(int rt) {
	for(int i=1;i<20;i++) {
		fa1[rt][i] = fa1[fa1[rt][i-1]][i-1];
		if( sum[rt][i-1] != INF && sum[fa1[rt][i-1]][i-1] != INF )
			sum[rt][i] = sum[rt][i-1] + sum[fa1[rt][i-1]][i-1];
		else sum[rt][i] = INF;
	}
	for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
		fa1[p->to][0] = rt;
		sum[p->to][0] = w[rt];
		dfs(p->to);
	}
}
int UpTo(int u, ll lim, int dep) {
	dep--; lim -= w[u];
    for(int i=19;i>=0;i--) {
        if( sum[u][i] <= lim && dep >= (1<<i) ) {
            lim -= sum[u][i];
            dep -= (1<<i);
            u = fa1[u][i];
        }
    }
    return u;
}
int ans = 0;
void dfs3(int rt) {
	for(edge *p=adj[rt];p!=NULL;p=p->nxt)
		dfs3(p->to);
	if( !Query(1, fir[rt]) ) {
		TModify(rt, UpTo(rt, S, L));
		ans++;
	}
}
int main() {
	scanf("%d%d%I64d", &n, &L, &S);
	for(int i=1;i<=n;i++)
		scanf("%I64d", &w[i]);
	for(int i=2;i<=n;i++) {
		int p;
		scanf("%d", &p);
		addedge(p, i);
	}
	for(int i=1;i<=n;i++) {
		if( w[i] > S ) {
			printf("%d\n", -1);
			return 0;
		}
	}
	for(int i=0;i<20;i++) {
        sum[0][i] = (1LL<<60);
        fa1[0][i] = 0;
    }
    sum[1][0] = (1LL<<60);
	dfs1(1, 0); dfs2(1, 1); Build(1, 1, n); dfs(1); dfs3(1);
	printf("%d\n", ans);
}

@END@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

猜你喜欢

转载自blog.csdn.net/Tiw_Air_Op1721/article/details/82953532