zoukankan      html  css  js  c++  java
  • 题解[Luogu P5008 [yLOI2018] 锦鲤抄]

    题目

    Link

    这首歌很好听。

    就是因为喜欢歌才来做这道题的

    一道很不错的贪心题,对思维的启发极大。

    Sol

    Sub1

    暴搜即可。

    是不是有模拟赛题解那味了

    Sub2

    这个(subtask)十分关键,直接让我们有通向正解的思路。

    发现,如果图是一个有向无环图,那我们根据反向拓扑序来选点,只要不是一开始就没入度的点,一定是可以选的。这样就为我们提供了做法:除去本无入度的点以外,其他点随便选。

    正是这一个有向无环图 ,让我们往缩点方向思考。

    正解

    手动画两个图,两个图都带一个环,一个环有入度,一个环没入度:

    有入度的:

    按照(1->4->3->2)的顺序可以取遍所有点。

    没入度的:

    无论怎样都有一个点取不到。

    那我们的结论就有了:

    缩点以后,有入度的环里的点随便选,其他的环舍掉最小的不选。

    Talk is cheap,show you the code.

    Code

    #include<bits/stdc++.h>
    #define M (2000010)
    #define N (2000010)
    using namespace std;
    struct xbk{int ed,nx;}e[M];
    int n,m,k,top,cnt,tot,ans,color,x[N],y[N],v[N];
    int head[N],rd[N],dfn[N],low[N],stk[N],col[N];
    bool used[N];
    priority_queue<int>q;
    vector<int>son[N];
    inline int read(){
    	int w=0;
    	char ch=getchar();
    	while(ch>'9'||ch<'0') ch=getchar();
    	while(ch>='0'&&ch<='9'){
    		w=(w<<3)+(w<<1)+(ch^48);
    		ch=getchar();
    	}
    	return w;
    }
    inline void add(int a,int b){
    	e[++cnt].ed=b;
    	e[cnt].nx=head[a];
    	head[a]=cnt;
    }
    inline void Tarjan(int st){
    	dfn[st]=low[st]=++tot;
    	used[st]=1,stk[++top]=st;
    	for(int i=head[st];i;i=e[i].nx){
    		int ed=e[i].ed;
    		if(!dfn[ed]){
    			Tarjan(ed);
    			low[st]=min(low[st],low[ed]);
    		}
    		else if(used[ed]) low[st]=min(low[st],dfn[ed]);
    	}
    	if(dfn[st]==low[st]){
    		color++;
    		while(stk[top+1]!=st){
    			son[color].push_back(stk[top]);
    			col[stk[top]]=color;
    			used[stk[top--]]=0;
    		}
    	}
    	return;
    }
    int main(){
    	n=read(),m=read(),k=read();
    	for(int i=1;i<=n;i++) v[i]=read();
    	for(int i=1;i<=m;i++){
    		x[i]=read(),y[i]=read();
    		add(x[i],y[i]);
    	}
    	for(int i=1;i<=n;i++)
    		if(!dfn[i]) Tarjan(i);
    	for(int i=1;i<=m;i++)
    		if(col[x[i]]!=col[y[i]]) rd[col[y[i]]]++;
    	for(int i=1;i<=color;i++){
    		if(rd[i])
    			for(int j=0;j<(int)son[i].size();j++) q.push(v[son[i][j]]);
    		else{
    			int mn=v[son[i][0]];
    			for(int j=1;j<(int)son[i].size();j++){
    				if(v[son[i][j]]>=mn) q.push(v[son[i][j]]);
    				else q.push(mn),mn=v[son[i][j]];
    			}
    		}
    	}
    	for(int i=1;i<=k;i++){
    		ans+=q.top(),q.pop();
    		if(q.empty()) break;
    	}		
    	printf("%d
    ",ans);
    	return 0;
    }
    

    完结撒花❀

  • 相关阅读:
    2015年中国互联网十大牛逼词汇
    考上好大学,然后进入IT行业是穷人孩子晋级中产的唯一出路?
    Redis中hash表中的field的value自增可以用hincrby
    如何学好编程(四)——这种方法真的有用吗
    挑战编程题(三)
    C++ 单链表的基本算法
    挑战编程题(二)
    程序员到底需要学习多少东西??
    Erlang 进制转换
    Erlang基础知识集锦
  • 原文地址:https://www.cnblogs.com/xxbbkk/p/14617752.html
Copyright © 2011-2022 走看看