解题思路
题目大意是给你一个有向图,选择 k + 1 k+1 k+1个能获取最大价值的联通块的价值总和。
显然 T a r j a n 缩 点 + 拓 扑 D P Tarjan缩点+拓扑DP Tarjan缩点+拓扑DP就可以解决啦!(DP前先做了个并查集方便答案统计)
具体过程:用Tarjan缩点,如果原路径没有被缩成一个点就相连,按这样的方式重新建图,记录入读。然后拓扑DP,dp[i]记录按照拓扑序走到第i个点的最大价值,ans[i]记录一个岛上(即一个联通块)的最大价值。最后ans从大到小快排,输出前 k + 1 k+1 k+1个的和
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ldb long double
using namespace std;
int n,m,k,K,tot,top,t,ans1;
int u[1000010],v[1000010],head[100010],dfn[100010],low[100010],st[100010],w[100010],c[100010],sum[100010],vis[100010];
int du[100010],fa[100010],size[100010],dp[100010],ans[100010];
struct c{
int x,next;
}a[2000010];
void add(int x,int y)
{
a[++k].x=y;
a[k].next=head[x];
head[x]=k;
}
int getfa(int x)
{
if(fa[x]==x)return x;
else return fa[x]=getfa(fa[x]);
}
void tarjan(int x)
{
dfn[x]=low[x]=++t;
st[++top]=x;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].x;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(!c[y])
low[x]=min(low[x],low[y]);
}
if(dfn[x]==low[x])
{
c[x]=++tot;
sum[tot]+=w[x];
while(st[top]!=x)
sum[tot]+=w[st[top]],c[st[top--]]=tot;
top--;
}
}
void work(){
queue<int> q;
for(int i=1;i<=tot;i++)
if(!du[i])q.push(i);
while(!q.empty())
{
int x=q.front();
q.pop();
dp[x]+=sum[x];
int k=getfa(x);
ans[k]=max(ans[k],dp[x]);
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].x;
du[y]--;
dp[y]=max(dp[y],dp[x]);
if(du[y]==0)
q.push(y);
}
}
}
int main(){
freopen("azeroth.in","r",stdin);
freopen("azeroth.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
add(u[i],v[i]);
}
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=n;i++)
{
if(!dfn[i])
tarjan(i);
}
for(int i=1;i<=tot;i++)
fa[i]=i,size[i]=1;
memset(a,0,sizeof(a));
memset(head,0,sizeof(head));
k=0;
for(int i=1;i<=m;i++)
{
if(c[u[i]]!=c[v[i]])
{
add(c[u[i]],c[v[i]]),du[c[v[i]]]++;
int x=getfa(c[u[i]]),y=getfa(c[v[i]]);
if(x!=y)
{
if(size[x]>size[y])
fa[y]=x,size[x]+=size[y];
else fa[x]=y,size[y]+=size[x];
}
}
}
work();
sort(ans+1,ans+n+1);
scanf("%d",&K);
for(int i=n;i>=max(n-K,1);i--)
ans1+=ans[i];
printf("%d",ans1);
}