[APIO2009]抢掠计划(强连通分量+缩点+拓扑排序+dp)

来源:互联网 发布:室内设计有什么软件 编辑:程序博客网 时间:2024/06/06 08:26
题意:

给定一个有向图,从指定起点出发,到任意一个指定终点停止,求经过的所有结点的最大点权和。点数、边数<=500000


因为一个强连通分量内的点相互可达,所以如果要经过其中一个点,就应经过它所在的强连通分量内的所有点,因此将一个强连通分量缩成一个点 
这样得到了一个有向无环图,在图上dp即可 

先对所有起点可达的点做一遍拓扑排序,然后用队列维护入度为0的点,每次取出队首元素,用它的答案更新其他点即可 


好像有一个点会RE?


#include<stdio.h>#include<stdlib.h>#include<string.h>int u[500005],v[500005],w[500005],first[500005],next[500005],bar[500005],f[500005],d[500005],vis[500005];int pre[500005],link[500005],sta[500005],sccno[500005],q[500005];int clo=0,top=0;void find(int x){int i,y;pre[x]=link[x]=++clo;sta[++top]=x;for(i=first[x];i!=0;i=next[i])if(sccno[v[i]]==0){if(pre[v[i]]==0) find(v[i]);if(link[x]>link[v[i]]) link[x]=link[v[i]];}if(pre[x]==link[x]){while(sta[top]!=x) sccno[sta[top--]]=x;sccno[sta[top--]]=x;}}void dfs(int x){int i;vis[x]=1;for(i=first[x];i!=0;i=next[i])if(vis[v[i]]==0) dfs(v[i]);}int main(){int n,m,s,p,i,head=0,tail=0,max=0;scanf("%d%d",&n,&m);for(i=1;i<=m;i++){scanf("%d%d",&u[i],&v[i]);next[i]=first[u[i]];first[u[i]]=i;}for(i=1;i<=n;i++)scanf("%d",&w[i]);scanf("%d%d",&s,&p);for(i=1;i<=p;i++)scanf("%d",&bar[i]);for(i=1;i<=n;i++)if(sccno[i]==0) find(i);//计算强连通分量 memset(first,0,sizeof(first));memset(next,0,sizeof(next));for(i=1;i<=m;i++)//缩点 {u[i]=sccno[u[i]];v[i]=sccno[v[i]];if(u[i]!=v[i]){next[i]=first[u[i]];first[u[i]]=i;}}for(i=1;i<=n;i++)if(sccno[i]!=i) w[sccno[i]]+=w[i];dfs(sccno[s]);for(i=1;i<=m;i++)if(u[i]!=v[i]&&vis[u[i]]==1) f[v[i]]++;q[tail++]=sccno[s];while(head<tail){d[q[head]]+=w[q[head]];for(i=first[q[head]];i!=0;i=next[i]){if(d[v[i]]<d[u[i]]) d[v[i]]=d[u[i]];f[v[i]]--;if(f[v[i]]==0) q[tail++]=v[i];}head++;}for(i=1;i<=p;i++)if(max<d[sccno[bar[i]]]) max=d[sccno[bar[i]]];printf("%d",max);return 0;}


0 0
原创粉丝点击