首先我们看到数据范围。妈耶!数据这么大,一开始还想用个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. TensorFlow入门(五)多层 LSTM 通俗易懂版

    欢迎转载,但请务必注明原文出处及作者信息. @author: huangyongye @creat_date: 2017-03-09 前言: 根据我本人学习 TensorFlow 实现 LSTM 的经 ...

  2. [Socket]Socket文件传输

    1.Server import java.io.DataInputStream; import java.io.FileOutputStream; import java.io.IOException ...

  3. Python基础(下)

    前言 print("\n".join([''.join(['*'*((x-y)%3) if((x*0.05)**2+(y*0.1)**2 -1)**3-(x*0.05)**2*(y ...

  4. HTML 5 拖放

    拖放(Drag 和 drop)是 HTML5 标准的组成部分. 拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放. 拖放事件 1. ...

  5. Python_函数_复习_习题_24

    # 函数 # 可读性强 复用性强# def 函数名(): # 函数体 #return 返回值# 所有的函数 只定义不调用就一定不执行 #先定义后调用 #函数名() #不接收返回值#返回值 = 函数名( ...

  6. 【MOOC EXP】Linux内核分析实验八报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的切换和系统的一般执行过程 知识点 ...

  7. Linux内核第二节

    作者:武西垚 深入理解函数调用堆栈 堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间 堆栈的作用 函数调用框架 传递参数 保存返回地址 提供局部变量空间 堆栈相关的寄存器 esp,堆栈指针,指 ...

  8. Zookeeper 3.4.8分布式安装

    1.机器信息 五台centos 64位机器 2.集群规划 Server Name Hadoop Cluster Zookeeper   Ensemble HBase Cluster Hadoop01 ...

  9. 『编程题全队』Scrum 冲刺博客

    1.介绍小组新加入的成员,Ta担任的角色 Answer: 我们小组的倪兢飞同学决定跳槽到团队あ,我们小组开了一个简短而又严肃的会议,满足倪兢飞同学的意愿,并感谢他为团队做出的巨大贡献.虽然我们遗失了一 ...

  10. VUE的语法笔记

    v-model = 'content' {{contents}} //vue 双向视图的绑定 v-text 只能返回一个文本内容 v-html 不仅可以返回文本内容还可以返回html标签 v-for ...