【Luogu】P3387缩点(Tarjan缩点+深搜DP)

 题没什么好说的,因为是模板题。求值我用的是dfs。

   不能直接在原图上dfs,因为原图上有环的话会发生一些滑稽的事情。所以我们要用Tarjan缩点。因为此题点权全为正,所以如果在图上走一个环当然可以全走一遍,结果当然是更优的。于是可以把环当成一个点来dfs,把它们的点权都加起来当成一个大点。

   然后就是求值。原图已经变成一张有向无环图,所以可以用拓扑排序求值,也可以枚举每个还没有被dfs到的点,然后dfs统计答案。dfs的时间是O(n),因为每个点最多被遍历一次。

   代码如下

#include<cstdio>
#include<cctype>
#include<cstdlib>

inline long long read(){
   long long num=0,f=1;
   char ch=getchar();
   while(!isdigit(ch)){
       if(ch=='-')f=-1;
       ch=getchar();
   }
   while(isdigit(ch)){
       num=num*10+ch-'0';
       ch=getchar();
   }
   return num*f;
}

struct Edge{
   int next,to;
};
struct Pic{
   Edge edge[200000];
   int head[100000],num;
   inline void add(int from,int to){
       edge[++num]=(Edge){head[from],to};
       head[from]=num;
   }
}Old,New;
int ans;
int     f[150000];
int   val[150000];
int   que[150000];
int   dfn[150000];
int   low[150000];
int   col[150000];
bool  vis[150000];
int stack[150000];
int ID,top,cnt;
void tarjan(int x){
   dfn[x]=low[x]=++ID;
   vis[x]=1;
   stack[++top]=x;
   for(int i=Old.head[x];i;i=Old.edge[i].next){
       int to=Old.edge[i].to;
       if(!dfn[to]){
           tarjan(to);
           low[x]=low[x]<low[to]?low[x]:low[to];
       }
       else if(vis[to])    low[x]=low[x]<dfn[to]?low[x]:dfn[to];
   }
   if(low[x]==dfn[x]){
       vis[x]=0;
       col[x]=++cnt;
       val[cnt]+=que[x];
       while(stack[top]!=x){
          col[stack[top--]]=cnt;
          val[cnt]+=que[stack[top+1]];
          vis[stack[top+1]]=0;
      }
      top--;
      }
}

int dfs(int x){
   if(f[x])return f[x];
   f[x]=val[x];
   int maxx=0;
   for(int i=New.head[x];i;i=New.edge[i].next){
       int to=New.edge[i].to;
       if(!f[to])dfs(to);
       maxx=maxx>f[to]?maxx:f[to];
   }
   f[x]+=maxx;
   return f[x];
}

int main(){
   int n=read(),m=read();
   for(int i=1;i<=n;++i)que[i]=read();
   int from,to;
   for(int i=1;i<=m;++i){
       from=read();to=read();
       Old.add(from,to);
   }
   for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
   for(int i=1;i<=n;++i){
       for(int j=Old.head[i];j;j=Old.edge[j].next)    if(col[i]!=col[Old.edge[j].to])    New.add(col[i],col[Old.edge[j].to]);
   }
   for(int i=1;i<=cnt;++i)
       if(!f[i]){
           dfs(i);
           ans=ans>f[i]?ans:f[i];
       }
   printf("%d",ans);
}
原文地址:https://www.cnblogs.com/cellular-automaton/p/7467397.html