zoukankan      html  css  js  c++  java
  • P3387 【模板】缩点


    P3387 【模板】缩点



    时间限制 1.00s
    内存限制 125.00MB


    题目背景


    缩点+DP


    题目描述


    给定一个\(n\)个点\(m\)条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

    允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。


    输入格式


    第一行两个正整数\(n,m\)

    第二行\(n\)个整数,依次代表点权

    第三至\(m+2\)行,每行两个整数\(u,v\),表示一条\(u \rightarrow v\) 的有向边。


    输出格式


    共一行,最大的点权之和。


    输入输出样例


    输入 #1

    2 2
    1 1
    1 2
    2 1
    

    输出 #2

    2
    

    说明/提示


    【数据范围】

    对于\(100\%\)的数据,\(1\le n \le 10^4\)\(1\le m \le 10^5\),点权\(\in [0,1000]\)

    算法:Tarjan 缩点 + DAGdp



    推荐hyfhaha【题解】3387缩点


    代码


    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<vector>
    using namespace std;
    const int N=1e5+5;
    int n,m,cnt,tot,ans,w[N],dfn[N],low[N],stc[N],dis[N],in[N],belong[N],U[N],V[N];
    bool vis[N];
    queue<int>q;
    stack<int>s;
    vector<int>e[N],E[N];
    void tarjan(int u){
    	dfn[u]=low[u]=++cnt;
    	s.push(u); vis[u]=1;
    	for(int v,i=0;i<e[u].size();++i){
    		v=e[u][i];
    		if(!dfn[v]){
    			tarjan(v);
    			low[u]>low[v] ? low[u]=low[v] : 1;
    		} else if(vis[v])
    			low[u]>dfn[v] ? low[u]=dfn[v] : 1;
    	}
    	if(dfn[u]==low[u]){
    		++tot;
    		do{
    			u=s.top(); s.pop(); vis[u]=0;
    			belong[u]=tot;
    			stc[tot]+=w[u];
    		}while(dfn[u]!=low[u]);
    	}
    }
    void spfa(int u){
    	q.push(u); vis[u]=1; dis[u]=stc[u];
    	while(!q.empty()){
    		u=q.front(); q.pop(); vis[u]=0;
    		for(int v,i=0;i<E[u].size();++i){
    			v=E[u][i];
    			if(dis[v]<dis[u]+stc[v]){
    				dis[v]=dis[u]+stc[v];
    				if(!vis[v]){ q.push(v); vis[v]=1; }
    			}
    		}
    	}
    	for(int i=1;i<=tot;++i) ans=max(ans,dis[i]);
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    	for(int i=1;i<=m;++i){
    		scanf("%d %d",&U[i],&V[i]);
    		e[U[i]].push_back(V[i]);
    	}
    	for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    	for(int i=1;i<=m;++i)
    		if(belong[U[i]]!=belong[V[i]]){
    			E[belong[U[i]]].push_back(belong[V[i]]);
    			++in[belong[V[i]]];
    		}
    	for(int i=1;i<=tot;++i)
    		if(!in[i]) spfa(i);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    HTML标签语义化对照表
    C#自定义分页控件3.0
    并发小工具
    C#方法
    我所知道的一个简单类
    等快递无聊旋转字符串
    委托
    撒列实现关键字过滤,速度可快了
    垃圾回收代
    递归再一次让哥震惊了
  • 原文地址:https://www.cnblogs.com/Potrem/p/Luogu_P3387.html
Copyright © 2011-2022 走看看