首先我们看到数据范围。妈耶!数据这么大,一开始还想用个DP来做,但是看着就不行,那么根据这个数据范围,我们大致可以猜到这道题的算法是一个贪心,那么我们怎么贪呢?

我们首先还是先画一个图:

样例解释一下:

我们取的点是\(3\),\(5\),\(7\)。

看到题目,因为\(1\)号节点的入度为0,那么就一定不能选择\(1\)号节点,那么接下来可以供我们选择的最大的权值的点也就只有\(3\),\(5\),\(7\)号节点,那么我们就来一个贪心策略:对每一个节点的权值进行排序,然后将所有不能取的节点全部不算,剩下的就都取最大的那几个。

以下是\(30\)分骗分程序

# include <bits/stdc++.h>
# define Ri register int
# define for1(i,a,b) for(Ri i(a);i<=b;++i)
# define for2(i,a,b) for(Ri i(a);i>=b;--i)

using namespace std;

inline int read ()
{
    int w = 0,x = 0;
    char ch = 0;
    while (!isdigit(ch)) { w |= ch =='-'; ch = getchar();}
    while (isdigit(ch)) { x = (x<<1) + (x<<3) + (ch ^ 48); ch = getchar(); }
    return w ? -x : x;
}

const int Maxm = 2000004;
const int Maxn = 5000004;

int Nedge, n, m, k;
int head[Maxm], ind[Maxn];

struct node{
    int v ,id ,ind ;
}a[5000004];

bool cmp (node a,node b)
{
    return a.v > b.v;
}

int main()
{
    n = read(),m = read(),k = read(); Nedge = 1;
    for1(i ,1 ,n ) a[i].v = read(),a[i].id = i;
    for1(i ,1 ,m )
    {
        int u = read(),v = read();
        a[v].ind ++;
    }
    sort (a + 1 , a + 1 + n , cmp);
    int cnt = 0 , ans = 0;
    for1(i ,1 ,n)
    {
        if (a[i].ind == 0) continue;
        else
        {
            ans += a[i].v;
            cnt ++;
            if (cnt == k) break;
        }
    }
    printf ("%d\n", ans);
    return 0;
}

但是这个贪心一定是错的。

为什么

我们来想一下,如果可以去掉的节点,是某一个接下来可以取的节点的唯一一个入边来源,那么这个一定会影响后面的答案,这个点也就不取了,所以我们就不能这样做。


那么我们应该怎么做呢?

这个时候我们就需要 胆大心细地思考题目了。我是好好听了出征大会的

其实也就只需要在这个贪心的基础上,加上一个契机,这个契机就是让当前这个删去的点,可以不对后面的点产生影响。

正解策略是:我们首先缩点,然后找到入度为0的环,删去这个环中权值最小的点,然后从小到大排序,取前k大的点。

我们先给一个缩点的模板吧!

inline void tarjan(int u)
{
    dfn[u] = low[u] = ++ dep;
    vis[u] = 1;
    S[top++] = u;
    for (int i = head[u]; i != -1; i = edge[i].next )
    {
        int v = edge[i].to;
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = Min(low[u] ,low[v]);
        }
        else if (vis[v]) low[u] = Min(low[u] ,low[v]);
    }
    int j;
    if (dfn[u] == low[u])
    {
        sum ++;
        do
        {
            j = S[ -- top];
            belong[j] = sum ;
            vis [j] = 0;
        }while (u != j) ;
    }
}

解释

那么我们就需要一个手段,使得这个这个点成为一个删去和不删去,不会影响答案得到东西:这个玩意的名字叫做缩点。

为什么我们会想到缩点,我们得从DAG中的环开始说起。

早在。。因为在有向图中,每一个点都是可以互相到达的,那么所以这个有向图中的每一个点都是有入度的,没有人反驳吧!,所以这个里面的点都是可以随意取的,但是要注意attention:当你这这个环是\(0\)的入度时,那么你就不能随意取掉最后一个点了,因为你这个最后一个点可能就没有入度了,那么我们为了保证所有的点都可以取到,我们就将这个环内的权值最小的点删去,那么这样就可以保证这个环断开后,这个点集中的点就可以随便取了。

那么还有一个问题,也就是如果是一个节点的缩点?

其实也是一个道理,我就不解释了,也就是把这个点删掉,反正这个点完全没有用。


以下是AC代码(新的码风本人感觉还是挺好看的QAQ)

# include <bits/stdc++.h>
# define Ri register int
# define for1(i,a,b) for(Ri i(a);i<=b;++i)
# define for2(i,a,b) for(Ri i(a);i>=b;--i)
# define ms(a,b) memset(a,b,sizeof(a))

using namespace std;

typedef long long LL;

const int M = 2000005;

struct Edge{
    int to ,next;
}edge[M];

int dfn[M], vis[M], low[M], S[M], head[M] ,belong[M] ,ind[M];
int dep, top, sum , n ,m ,k ,Nedge;

struct node{
    int v ,id ;
}a[M];

inline int read() //快读
{
    int w = 0,x = 0;
    char ch = 0;
    while (!isdigit(ch))
    {
        w |= ch == '-';
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return w ? -x : x ;
}

inline int Min(int n,int m) //三目取min
{
    return n < m ? n : m;
}

inline void Add_Edge(int u ,int v) //链式前向星
{
    edge[Nedge] = (Edge) {v ,head[u]} ;
    head[u] = Nedge++;
}

inline void tarjan(int u) //tarjan缩点模板
{
    dfn[u] = low[u] = ++ dep;
    vis[u] = 1;
    S[top++] = u;
    for (int i = head[u]; i != -1; i = edge[i].next )
    {
        int v = edge[i].to;
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = Min(low[u] ,low[v]);
        }
        else if (vis[v]) low[u] = Min(low[u] ,low[v]);
    }
    int j;
    if (dfn[u] == low[u])
    {
        sum ++;
        do
        {
            j = S[ -- top];
            belong[j] = sum ;
            vis [j] = 0;
        }while (u != j) ;
    }
}

inline bool cmp1(node a,node b) //从小到大排序
{
    return a.v < b.v;
}

inline bool cmp2(node a,node b) //从大到小排序
{
    return a.v > b.v;
}

int main()
{
    ms(head ,-1);
    ms(dfn ,0);
    ms(vis ,0);
    ms(belong ,0);
    sum = 0,dep = 0,top = 0;
    n = read(),m = read(),k = read();
    for1(i ,1 ,n) a[i].v = read(),a[i].id = i;
    for1(i ,1 ,m)
    {
        int u = read(),v = read();
        Add_Edge(u , v);
    }
    for1(i ,1 ,n)
    {
        if (!dfn[i]) tarjan(i); // 缩一波点
    }
    for1(i ,1 ,n)
    {
        for (int j = head[i]; j != -1; j = edge[j].next)
        {
            int v = edge[j].to;
            if ( belong[i] != belong[v] ) ind[belong[v]] ++;
        }
    }//统计当前缩完点后的每个点的入度
    sort(a + 1, a + 1 + n ,cmp1);
    for1(i ,1 ,n)
    {
        if (ind[belong[a[i].id]] == 0)
        {
            a[i].v = 0;
            ind[belong[a[i].id]] = 1;
        }
    }//删去一个联通块中权值最小的点
    sort(a + 1 , a + 1 + n ,cmp2);
    LL ans = 0, cnt = 0;
    for1(i ,1 ,n) //计算我们的答案
    {
        ans += a[i].v;
        cnt ++ ;
        if (cnt == k) break;
    }
    printf ("%lld\n", ans);
    return 0;
}

[luogu5008]逛庭院的更多相关文章

  1. luogu5008 逛庭院 (tarjan缩点)

    首先如果这是一个DAG,我按照拓扑序倒着去选,一定能选到所有入度不为0的点 然后考虑有环的情况 我们拎出来一个强连通分量 先假设它缩点以后是没有入度的 那我最后它里面一定至少剩一个不能选 因为就剩一个 ...

  2. 【洛谷5008】逛庭院(Tarjan,贪心)

    [洛谷5008]逛庭院(Tarjan,贪心) 题面 洛谷 题解 如果图是一个\(DAG\),我们可以任意选择若干个不是入度为\(0\)的点,然后把它们按照拓扑序倒序删掉,不难证明这样一定是合法的. 现 ...

  3. Luogu P5008 逛庭院

    题目传送门 我校神仙出的神仙题 \(\%\%\%\) 30分 找出所有有入度的点,排序,选前\(k\)个点,好了,30分到手. #include<iostream> #include< ...

  4. 【洛谷P5008 逛庭院】tarjan缩点+贪心

    既然没有题解,那么我就来提供给一份. -- 首先我们看到数据范围.妈耶!数据这么大,一开始还想用个DP来做,但是看着就不行,那么根据这个数据范围,我们大致可以猜到这道题的算法是一个贪心,那么我们怎么贪 ...

  5. 水果姐逛水果街Ⅱ codevs 3305

    3305 水果姐逛水果街Ⅱ  时间限制: 2 s  空间限制: 256000 KB   题目描述 Description 水果姐第二天心情也很不错,又来逛水果街. 突然,cgh又出现了.cgh施展了魔 ...

  6. codevs3305 水果姐逛水果街Ⅱ

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...

  7. [vijos P1083] 小白逛公园

    不知怎地竟有种错觉此题最近做过= =目测是类似的?那道题貌似是纯动归? 本来今晚想做两道题的,一道是本题,一道是P1653疯狂的方格取数或NOI08 Employee,看看现在的时间目测这个目标又达不 ...

  8. Bzoj 1756: Vijos1083 小白逛公园 线段树

    1756: Vijos1083 小白逛公园 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1021  Solved: 326[Submit][Statu ...

  9. Codevs 3305 水果姐逛水果街Ⅱ 倍增LCA

    题目:http://codevs.cn/problem/3305/  时间限制: 2 s   空间限制: 256000 KB   题目等级 : 钻石 Diamond 题解       题目描述 Des ...

随机推荐

  1. Luogu P3768 简单的数学题

    非常恶心的一道数学题,推式子推到吐血. 光是\(\gcd\)求和我还是会的,但是多了个\(ij\)是什么鬼东西. \[\sum_{i=1}^n\sum_{j=1}^nij\gcd(i,j)=\sum_ ...

  2. python3通过gevent.pool限制协程并发数量

    协程虽然是轻量级的线程,但到达一定数量后,仍然会造成服务器崩溃出错.最好的方法通过限制协程并发数量来解决此类问题. server代码: #!/usr/bin/env python # -*- codi ...

  3. C# 根据部分属性来判断俩个对象是否相同

    根据部分属性来判断俩个对象是否相同 代码是第一版本 可能不牢固 有问题请反馈一下 3QU 效果图: public static class CustomExpand { public static b ...

  4. LeetCode Pow(x, n) (快速幂)

    题意 Implement pow(x, n). 求X的N次方. 解法 用正常的办法来做是会超时的,因为可能有21亿次方的情况,所以需要优化一下.这里用到了快速幂算法,简单来说就是将指数分解成二进制的形 ...

  5. 开启mac上印象笔记的代码块

    Mac 印象笔记左上角菜单栏:偏好设置-->软件更新-->开启代码块 (Preferences -> Software Update -> Enable code block) ...

  6. Zabbix监控系统部署:前端初始化

    1. 概述 在上一篇博客<Zabbix监控系统部署:源码安装.md>中,主要进行了zabbix最新版的源码编译安装. (博客园地址:https://www.cnblogs.com/liwa ...

  7. Asp.Net_ 服务端向客户端写JavaScript脚本

    在Asp.net 服务端处理脚本,一般都用 ClientScriptManager ,即web窗体服务端的this.ClientScript.该对象比较常用的方法: 1.RegisterArrayDe ...

  8. 变量 var &函数new

    声明变量 变量:变量是存储信息的容器,创建变量通常称为"声明"变量 变量必须以字母开头(小驼峰式myName): 变量也能以 $ 和 _ 符号开头(不过我们不推荐这么做): 变量名 ...

  9. A. Make a triangle!

    题意 给你三条边a,b,c问使得构成三角形,需要增加的最少长度是多少 思路 数学了啦 代码 #include<bits/stdc++.h> using namespace std; #de ...

  10. 07-java学习-方法重载-idea集成开发工具学习-项目-模块-包

    方法重载的概念? 方法重载的好处? 集成开发工具idea的学习 下载 安装 设置 建项目 导入项目 建模块 导入模块 建包 复制粘贴包 建类 复制粘贴类 运行 调试