Talking point reduction

Pre Cheese: Tarjan seeking strongly connected components

For a directed graph of the two points, for \ (V_i-> V_j \) there is an edge and \ (V_j-> V_i \) there is an edge (i.e., able to reach each other), is a strongly connected components (not limited to at two points)

We can use the \ (Tarjan \) obtaining an all strongly connected components in the figure.

So, in some of the drawings may be strongly connected components shrink to a point. And it makes a mark.

Example: \ (Luogu \) \ (P3387 \)

For this question, we seek the path when in a strongly connected component weight, since you can reach each other, and FIG seek the largest path, we will direct it shrunk a point where the cumulative weight and i.e. can.

When condensing point, we can make a mark, a mark ring to a point, it is taken for convenience \ (Tarjan \) the first point (the bottom of the stack) in the strongly connected components as the number of re-encountered.

So we do not even side of the time, you can multi-point record at both ends of an edge. End point reduction when re-drawing even again, this time we need to use the maintenance.

Two sides to the original point, if not a strongly connected component, links again.

How to do the most path?

We set \ (dis [i] \) is \ (i \) as the starting point of the longest path, then:

Traversing over point \ (i \) even point \ (J \) , then:

\ (DIS [j] = max (DIS [j], DIS [I] + Val [j]) \) , \ (Val [j] \) is the weight value (within the loop j where or \ (j \) point right)

Finally, all point to take it again to the maximum.

\(Code:\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=500000;
int id,si[MAXN],head[MAXN],n,m,cnt,tot,val[MAXN],count,h[MAXN];
int low[MAXN],dfn[MAXN],top,st[MAXN],inst[MAXN],in[MAXN],dis[MAXN];
struct edge{
    int nxt,to,pre;
}e[MAXN],g[MAXN];
inline void add(int x,int y){
    e[++tot].nxt=head[x];
    e[tot].to=y;
    e[tot].pre=x;//记录边的两点 
    head[x]=tot;
}
inline void kdd(int x,int y){
    g[++count].nxt=h[x];
    g[count].to=y;
    g[count].pre=x;//同上 
    h[x]=count;
}
void Tarjan(int x){
    st[++top]=x;low[x]=dfn[x]=++id;inst[x]=1;
    for(int i=head[x];i;i=e[i].nxt){
        int j=e[i].to;
        if(!dfn[j]){
            Tarjan(j);
            low[x]=min(low[x],low[j]);
        }
        else if(inst[j])low[x]=min(low[x],dfn[j]);
    }//正常Tarjan求强连通分量 
    if(low[x]==dfn[x]){
        int y;
        while(y=st[top--]){
            si[y]=x;//这里是把这个环都标记为x 
            inst[y]=0;//弹出栈了就标记一下 
            if(x==y)break;//找完了就跳出 
            val[x]+=val[y];//累计环内权值 
        }
    }
}
int solve(){
    queue<int>q;//队列维护一下 
    for(int i=1;i<=n;++i){
        if(si[i]==i&&!in[i]){//对于一个点如果它是这个环的编号且入度为0 
            q.push(i);//加入队列 
            dis[i]=val[i];//初始当然是环内权值和 
        }
    }
    while(!q.empty()){//如果队列非空就继续 
        int k=q.front();q.pop();//队头,弹出 
        for(int i=h[k];i;i=g[i].nxt){//链式前向星 
            int j=g[i].to;
            dis[j]=max(dis[j],dis[k]+val[j]);//对于一个点,能连接的就更新一下 
            in[j]--;//j更新完去掉一个入度 
            if(!in[j])q.push(j);//拓扑排序,可以了就入队 
        }
    }
    int Ans=0;//取Max做答案 
    for(int i=1;i<=n;++i)Ans=max(Ans,dis[i]);
    return Ans;//完结撒花 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&val[i]);
    for(int i=1,x,y;i<=m;++i){
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i);//每一个没有遍历到的点Tarjan一遍 
    for(int i=1;i<=m;++i){
        int ii=e[i].pre,jj=e[i].to;//边的两端 
        int x=si[ii],y=si[jj];//对于每一条边求出它们所在的环 
        if(x!=y){//不在同一条边就连边 
            kdd(x,y);//缩完点之后重新连边 
            in[y]++;//入度++ 
        }
    }
    printf("%d\n",solve());
    return 0;
}

Guess you like

Origin www.cnblogs.com/h-lka/p/11366595.html