zoukankan      html  css  js  c++  java
  • 【洛谷P5008】锦鲤抄

    题目

    题目链接:https://www.luogu.com.cn/problem/P5008
    给你一张有向图,每个点有一个点权。任意时刻你可以任意选择一个有入度的点,获得它的点权并把它和它的出边从图上删去。最多能选择 \(k\) 个点,求最多能获得多少点权。
    \(n\leq 5\times 10^5+4\)\(m\leq 2\times 10^6+4\)

    思路

    首先如果给定的图是一张 DAG,那么除了入度为 \(0\) 的点,其他点都按照拓扑序取,一定是可以取任意点的。所以只需要把入度不为 \(0\) 的点的点权排一下序就好了。
    对于任意一张有向图,先缩点,因为一个强连通分量一定可以选择其中任意一个点作为根,构造出一棵外向树,而外向树是属于 DAG 的,所以考虑每一个强连通分量:

    • 如果这个强连通分量有入度,那么把有入度的点看作是根,这个强连通分量里的点都可以取(其他点按照拓扑序,根最后取)。
    • 如果这个强连通分量没有入度,将点权最小的点作为根,这样其他点都可以去,显然这样是最优的。

    然后排个序取前 \(k\) 大就行了。
    时间复杂度 \(O(m+n\log n)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=500010,M=2000010;
    int n,m,k,tot,cnt,ans,a[N],dfn[N],low[N],head[N],bel[N],U[M],V[M];
    bool vis[N];
    vector<int> scc[N];
    stack<int> st;
    
    int read()
    {
    	int d=0; char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    struct edge
    {
    	int next,to;
    }e[M];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void tarjan(int x)
    {
    	dfn[x]=low[x]=++tot;
    	st.push(x); vis[x]=1;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!dfn[v])
    			tarjan(v),low[x]=min(low[x],low[v]);
    		else if (vis[v])
    			low[x]=min(low[x],dfn[v]);
    	}
    	if (dfn[x]==low[x])
    	{
    		cnt++;
    		while (st.top()!=x)
    		{
    			int y=st.top(); st.pop();
    			scc[cnt].push_back(a[y]);
    			vis[y]=0; bel[y]=cnt;
    		}
    		st.pop();
    		scc[cnt].push_back(a[x]);
    		vis[x]=0; bel[x]=cnt;
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	n=read(); m=read(); k=read();
    	for (int i=1;i<=n;i++) a[i]=read();
    	for (int i=1;i<=m;i++)
    	{
    		U[i]=read(); V[i]=read();
    		add(U[i],V[i]);
    	}
    	tot=0;
    	for (int i=1;i<=n;i++)
    		if (!dfn[i]) tarjan(i);
    	for (int i=1;i<=m;i++)
    		if (bel[U[i]]!=bel[V[i]]) vis[bel[V[i]]]=1;
    	tot=0;
    	for (int i=1;i<=cnt;i++)
    	{
    		sort(scc[i].begin(),scc[i].end());
    		for (int j=(!vis[i]);j<(int)scc[i].size();j++)
    			a[++tot]=scc[i][j];
    	}
    	sort(a+1,a+1+tot);
    	for (int i=tot;i>max(tot-k,0);i--) ans+=a[i];
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    My first blog in cnblog
    浅析JavaScript中this储存
    input 文本框密码框的只读属性
    Js 数组——filter()、map()、some()、every()、forEach()、lastIndexOf()、indexOf()
    jquery使用$.getJson()跨域大数据量请求方法
    JS中关于clientWidth offsetWidth scrollWidth 等的含义及区别
    JS性能优化
    npm 创建 node.js 项目
    css 垂直居中的几种方法
    字符串转数组
  • 原文地址:https://www.cnblogs.com/stoorz/p/15416044.html
Copyright © 2011-2022 走看看