【题解】【抢掠计划】&&【强联通分量缩点学习笔记】
作者:互联网
P3627 [APIO2009]抢掠计划
Solution:
首先这是一张有向图,点有点权,且给定一个起点,给定多个终点,询问从起点出发,在任意一个终点结束,所经过的点权和最大值
如果对于任意一条边,把它终点的点权作为该边的边权,那么只需从起点出发跑一个最长路就可以了
但问题是,边权都为正,一旦出现环,就无法跑最长路了
注意到一句话:“他可以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机里面就不会再有钱了。”,这说明每个点最多只能经过一次。
这道题我们可以用强联通分量和缩点解决
强联通图:对于一个有向图,若任意两个节点都可以互相到达,那么称该图为强连通图
强联通分量:对于一个有向图,它的“极大强联通子图”为强联通分量。一个图可能有多个强联通分量
缩点:对于图的每个强联通分量,在原图中用一个点表示,所形成的新图一定是有向无环图
于是我们就可以求出图的强联通分量,并缩点,再跑一个拓扑排序或spfa,因为没有环,所以可以跑最长路
注意缩点时要把原图中的点的数据集中到缩完后的点上,具体的集中方法根据题目要求灵活处理
Code
#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-'){ch=getchar();w=-1;}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); }
return x*w;
}
const int N=5e5+100;
int n,m,a[N],s,p,bar[N],low[N],dfn[N],c[N],d[N],barc[N],valc[N],deg[N];
int totc,tot,stk[N],ins[N],top;
vector<int>v[N];
struct node{
int nxt,to,w;
}e[N];
int head[N],cnt;
void add(int x,int y,int z)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
e[cnt].w=z;
head[x]=cnt;
}
void tarjan(int x)//求强联通分量模板
{
dfn[x]=low[x]=++tot;
stk[++top]=x;ins[x]=1;
for(int i=0;i<v[x].size();++i){
int y=v[x][i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
int y;
totc++;
do
{
y=stk[top--];
c[y]=totc;
ins[y]=0;
}while(y!=x);
}
}
void spfa()
{
queue<int>q;
int vis[N];
memset(vis,0,sizeof vis);
memset(d,0,sizeof d);
d[c[s]]=valc[c[s]];
vis[c[s]]=1;
q.push(c[s]);
while(q.size())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(d[y]<d[x]+e[i].w){
d[y]=d[x]+e[i].w;
if(vis[y]) continue;
q.push(y);
}
}
}
}
void topu()
{
queue<int>q;
memset(d,0xcf,sizeof d);
for(int i=1;i<=totc;++i){
if(deg[i]==0){
q.push(i);
if(c[s]==i) d[i]=valc[i];
}
}
while(q.size()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
deg[y]--;
if(d[x]>=0) d[y]=max(d[y],d[x]+e[i].w);
if(deg[y]==0){
q.push(y);
if(y==c[s]) d[y]=valc[y];
}
}
}
}
signed main()
{
n=read();m=read();
for(int i=1;i<=m;++i){
int x,y;
x=read();y=read();
v[x].push_back(y);
}
for(int i=1;i<=n;++i) a[i]=read();
s=read();p=read();
for(int i=1;i<=p;++i){
int x=read();
bar[x]=1;
}
for(int i=1;i<=n;++i){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;++i){
barc[c[i]]|=bar[i];
valc[c[i]]+=a[i];
}
for(int i=1;i<=n;++i){
for(int j=0;j<v[i].size();++j){
int y=v[i][j];
if(c[i]==c[y]) continue;
add(c[i],c[y],valc[c[y]]);
deg[c[y]]++;
}
}
// spfa();
topu();
int ans=0;
for(int i=1;i<=totc;++i){
if(barc[i]){
ans=max(ans,d[i]);
}
}
cout<<ans;
return 0;
}
标签:缩点,cnt,ch,联通,int,题解,&&,分量 来源: https://www.cnblogs.com/glq-Blog/p/15527173.html