树形dp
F - Select EdgesAtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.https://atcoder.jp/contests/abc259/tasks/abc259_f思路:我们先寻找状态,如果对于每个父亲节点考虑,会发现记录状态很麻烦,f[u][v][0/1],对于子节点是否进行连接.这样确定状态的话会爆内存,那么我们如果人要记录子节点与父亲节点是否相连这个状态,可以取消从父亲节点考虑,而从儿子节点考虑,f[v][0/1],f[v][0]状态表示为节点v没有一条边向上延伸的最大值,f[v][1]则为从节点v向上延伸一条边的最大值.
处理好了之后,只需要对于父亲节点进行连接即可.状态转移方程式如下:(这个式子只是大概模型)
我们不连接当前父亲节点的所有子节点,再将父亲节点和当前子节点进行链接产生的贡献进行排序,取前d[i]-1条进行链接,其余不连接,传的值还是f[v][0],连接到的点传的就是f[v][1].因为f[u][1]表示要延伸一条边网上,就要至少留一条边给父亲节点,需要注意的是,如果我链接这个之后的贡献小于不连接的贡献也就,没有必要链接了.f[u][0]就直接链接就可以了.
这里一定要搞清楚的是要不要链接,取对于
{f[v][1]+w-f[v][0]}
排序之后的数组,当>0就表示链接的贡献大于不连接的贡献.分别对于父节点的状态取最多前d[i]个和d[i-1]个进行链接就行了.
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
const int N =3e5+10,mod=998244353;
int d[N],f[N][2];
struct node
{
int to,ne,val;
}edge[2*N];
int h[N],tot=0;
void add(int x,int y,int w)
{
edge[++tot].to=y;
edge[tot].ne=h[x];
edge[tot].val=w;
h[x]=tot;
return ;
}
bool cmp(int a,int b)
{
return a>b;
}
void dfs(int u,int fa)
{
int sum=0;
vector<int>ve;
for(int i=h[u];i!=-1;i=edge[i].ne)
{
int v=edge[i].to;
if(v==fa)
continue;
dfs(v,u);
}
for(int i=h[u];i!=-1;i=edge[i].ne)
{
int v=edge[i].to;
int w=edge[i].val;
if(v==fa)
continue;
sum+=f[v][0];
ve.push_back({f[v][1]+w-f[v][0]});
}
sort(ve.begin(),ve.end(),cmp);
if(d[u]==0)
{
f[u][0]=sum;
f[u][1]=-1e18;
}
else
{
f[u][0]=f[u][1]=sum;
for(int i=0;i<ve.size();i++)
{
if(ve[i]<0)
break;
if(i<d[u])
f[u][0]+=ve[i];
if(i<d[u]-1)
f[u][1]+=ve[i];
}
}
return ;
}
void solve()
{
memset(h,-1,sizeof h);
int n,x,y,w;
cin>>n;
for(int i=1;i<=n;i++)
cin>>d[i];
for(int i=1;i<n;i++)
{
cin>>x>>y>>w;
add(x,y,w);
add(y,x,w);
}
dfs(1,-1);
cout<<f[1][0]<<"\n";
return;
}
signed main()
{
solve();
return 0;
}
注意细节处理