树链剖分找权值最大的前k条链
题目大意:给定一个树形的游戏网络,可以从根节点出发k个人,每个人可以沿着一条路径走下去,不能回头,出口在各个叶子节点,在路过一个节点时可以
获得该点的权值,每个点的权值只能被获得一次,问k个人怎样走最后可以获得的权值最多
解题思路:首先从反向建立一棵有向树(从叶子节点到根节点),首先dfs1找出每个节点到根节点的最大权路径,然后按权值递减排序,dfs2找每个点到根节点
题目大意:给定一个树形的游戏网络,可以从根节点出发k个人,每个人可以沿着一条路径走下去,不能回头,出口在各个叶子节点,在路过一个节点时可以
获得该点的权值,每个点的权值只能被获得一次,问k个人怎样走最后可以获得的权值最多
解题思路:首先从反向建立一棵有向树(从叶子节点到根节点),首先dfs1找出每个节点到根节点的最大权路径,然后按权值递减排序,dfs2找每个点到根节点
的最大权路径,走过的点不能重复走,最后在这些最大权路径中取前k大即为答案
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
#define mem(vis,a) memset(vis,a,sizeof(vis))
#define bug puts("**********");
#define N 100100
using namespace std;
struct node
{
int id;
LL sum;
friend bool operator <(node p1,node p2)
{
if(p1.sum!=p2.sum)
return p1.sum>p2.sum;
return p1.id<p2.id;
}
}p[N];
vector<int>vec[N];
int n,m;
int vis[N];
LL val[N];
LL a[N];
LL DFS(int x) ///构建出树的个点到根的权值 ,减枝==记忆化搜索
{
if(vis[x])return p[x].sum;
p[x].sum=val[x];
vis[x]=1;
p[x].id=x;
for(int i=0;i<vec[x].size();i++)
{
p[x].sum+=DFS(vec[x][i]);
}
return p[x].sum;
}
LL DFS2(int x)
{
if(vis[x])return 0;
LL sum=val[x];
vis[x]=1;
for(int i=0;i<vec[x].size();i++)
{
sum+=DFS2(vec[x][i]);
}
return sum;
}
bool cmp(LL x,LL y)
{
return x>y;
}
int main()
{
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
mem(vec,0);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&val[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
vec[y].push_back(x); ///反向建树
}
mem(vis,0);
for(int i=1;i<=n;i++)
{
if(!vis[i]) ///减枝
DFS(i);
}
mem(a,0); ///防止m>num 后遗留上次的算数值
mem(vis,0);
sort(p+1,p+n+1);
int num=0;
for(int i=1;i<=n;i++)
{
// if(!vis[p[i].id]) ///不要加这句话否则会使 m>num;
// {
a[num++]=DFS2(p[i].id);
//}
}
sort(a,a+num,cmp);
LL ans=0;
for(int i=0;i<m;i++)
ans+=a[i];
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}