首页 > 其他分享 >DreamOJ D10009

DreamOJ D10009

时间:2024-04-02 12:12:13浏览次数:26  
标签:DreamOJ D10009 return int tree st fa 节点

DreamOJ D10009

标签

DP 线段树 均摊

题意

给定一个包含 \(n\) 个节点根为 \(1\) 的树,和一个序列 \(s\) 。

如果满足以下两个条件,那么一个包含 \(k\) 条简单路径的可重集合认为是合法的。

  • 这个可重集合中所有的路径都是从根节点 \(1\) 出发。
  • 令 \(c_i\) 为覆盖节点 \(i\) 的路径数量,如果对于每对拥有相同父亲节点的节点 \((u,v)\) ,要求 \(|c_u-c_v| \leq 1\) 。

对于这个可重集合,其权值为 \(\sum_{i=1}^n c_is_i\) 。

可以证明,每组数据至少有一个可用的路径集合,请你找到所有合法的可重集合中的最大权值。

题解

正解1

首先,由于 \(s[i]\ge0\) 所以,可重集中的路径一定是越长越好的。我们不难证明,可重集中的每一条路径都是一条由1号节点到某个叶子节点的路径。

故一个相对暴力的做法就产生了:

我们对于每个节点动态开一个线段树,维护其所有儿子到其子树内叶子节点的最大权值。

接着用最大权值加上该节点本身的权值,转递给父节点建树。

对于每一次从根节点找路径的过程,我们从找到的叶子节点向上更新,把当前节点的贡献在父节点线段树中的位置赋为一个极小值,再递归父节点。这是为了在保证 \(|c_u-c_v| \leq 1\) 的情况下,每次找路径选择的都是被选择次数最少的子节点的贡献。当我们发现某个节点的线段树找出来的最大值是极小值时,这就意味着它的所有子节点的贡献都已经被选了一次,所以我们暴力重构该节点的线段树。

这样的复杂度直接与 \(k\) 挂钩,由于 \(k\) 很大,复杂度显然爆炸。考虑缩小 \(k\) 范围,由于每次找路径都是在子节点中轮流找路径,故不是每次找路径都要浪费线段树来暴力找。初始时,我们认为需要从1号节点找 \(k\) 条路径。我们遍历整棵树,把 \(k\div son\) 的次数交给子节点去找,视为一个子问题,该节点仅查找 \(k\%son\) 条路径(\(son\) 表示节点的儿子数量)。由于叶子节点无需继续查找,每个非叶子节点最多查儿子个数次路径,总次数与 \(n\) 同阶。然而重构操作仍然需要,比较难写。复杂度可以近似为 \(O(logn\times \sum_{i=1}^n son[i]\times d[i])\)(\(son[i]\) 表示儿子数量,\(d[i]\) 为节点深度)。根据yd所说,由于 \(son[i]\) 与 \(d[i]\) 成反比,总复杂度似乎可以均摊成正确的 \(O(nlogn)\)。

代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=2e5+10;
int T,n,k,a[N],b[N],pl[N],fa[N],ans,d[N];
vector<int>edge[N];
struct TREE{
	int mx,wch;
};
struct node{
	int siz;
	vector<int>us1,us2;
	int lson(int l,int r)
	{
		return (l+r)/2;
	}
	int rson(int l,int r)
	{
		return (l+r)/2+1;
	}
	TREE update(TREE x,TREE y)
	{
		if(x.mx<y.mx)
		return y;
		else
		return x;
	}
	void pushup(int x)
	{
		if(tree[x*2].mx>tree[x*2+1].mx)
		tree[x]=tree[x*2];
		else
		tree[x]=tree[x*2+1];
		return;
	}
	void build(int l,int r,int x)
	{
		if(l>r)
		return;
		if(l==r)
		{
			tree[x]=TREE{us1[l],us2[l]};
			return;
		}
		build(l,lson(l,r),x*2);
		build(rson(l,r),r,x*2+1);
		pushup(x);
		return;
	}
	void change(int l,int r,int x,int to,TREE w)
	{
		if(l==r)
		{
			tree[x]=w;
			return;
		}
		if(lson(l,r)>=to)
		change(l,lson(l,r),x*2,to,w);
		else
		change(rson(l,r),r,x*2+1,to,w);
        pushup(x);
		return;
	}
	vector<TREE>tree;
}t[N];
void dfs(int x)
{
	if(edge[x].size()==0)
	return;
	int w=b[x]/edge[x].size();
	b[x]%=edge[x].size();
	t[x].siz=edge[x].size();
	t[x].tree.resize(edge[x].size()*4+10);
	vector<int>us1(edge[x].size()+10);
	vector<int>us2(edge[x].size()+10);
	for(int i=0;i<edge[x].size();i++)
	{
		int to=edge[x][i];
		d[to]=d[x]+1;
		b[to]+=w;
		dfs(to);
		pl[to]=i+1;
		if(edge[to].size()==0)
		{
			us1[i+1]=a[to];
			us2[i+1]=to;
		}
		else
		{
			TREE xx=t[to].tree[1];
			us1[i+1]=a[to]+xx.mx;
			us2[i+1]=xx.wch;
		}
	}
	t[x].us1=us1;
	t[x].us2=us2;
	t[x].build(1,t[x].siz,1);
	return;
}
void check(int x)
{
	vector<int>us1(edge[x].size()+10);
	vector<int>us2(edge[x].size()+10);
	if(t[x].tree[1].mx==INT_MIN)
	{
		for(int i=0;i<edge[x].size();i++)
		{
			int to=edge[x][i];
			if(edge[to].size()==0)
			{
				us1[i+1]=a[to];
				us2[i+1]=to;
			}
			else
			{
				TREE xx=t[to].tree[1];
				us1[i+1]=a[to]+xx.mx;
				us2[i+1]=xx.wch;
			}
		}
		t[x].us1=us1;
		t[x].us2=us2;
		t[x].build(1,t[x].siz,1);
	}
	return;
}
void getnw(int st,int ed)
{
	if(d[fa[st]]>=d[ed])
	t[fa[st]].change(1,t[fa[st]].siz,1,pl[st],TREE{INT_MIN,0});
	else 
	t[fa[st]].change(1,t[fa[st]].siz,1,pl[st],TREE{t[st].tree[1].mx+a[st],t[st].tree[1].wch});
	check(fa[st]);	
	if(fa[st]!=1)
	getnw(fa[st],ed);
	return;
}
void dfs2(int x)
{
	for(int i=0;i<edge[x].size();i++)
	{
		int to=edge[x][i];
		dfs2(to);
	}
	if(edge[x].size()!=0)
	{
		for(int i=1;i<=b[x];i++)
		{
			TREE us=t[x].tree[1];
			b[us.wch]++;
			getnw(us.wch,x);
		}
		b[x]=0;
	}
	return;
}
void dfs3(int x)
{
	for(int i=0;i<edge[x].size();i++)
	{
		int to=edge[x][i];
		dfs3(to);
		b[x]+=b[to];
	}
	ans+=b[x]*a[x];
	return;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--)
	{
		cin>>n>>k;
		for(int i=1;i<=n;i++)
		{
			b[i]=0;
			edge[i].clear();
		}
		for(int i=2;i<=n;i++)
		{
			cin>>fa[i];
			edge[fa[i]].push_back(i);
		}
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		b[1]=k;
		dfs(1);
		dfs2(1);
		ans=0;
		dfs3(1);
		cout<<ans<<'\n';
	}
	return 0;
}
可能的出错点

出错点挺多,如果要写建议看看我的代码。

1.要开long long。

2.注意全局变量与局部变量。

3.注意update部分的细节。

正解2

标签:DreamOJ,D10009,return,int,tree,st,fa,节点
From: https://www.cnblogs.com/hahaxiang/p/18110303

相关文章