[luogu5008]逛庭院
首先我们看到数据范围。妈耶!数据这么大,一开始还想用个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]逛庭院的更多相关文章
- luogu5008 逛庭院 (tarjan缩点)
首先如果这是一个DAG,我按照拓扑序倒着去选,一定能选到所有入度不为0的点 然后考虑有环的情况 我们拎出来一个强连通分量 先假设它缩点以后是没有入度的 那我最后它里面一定至少剩一个不能选 因为就剩一个 ...
- 【洛谷5008】逛庭院(Tarjan,贪心)
[洛谷5008]逛庭院(Tarjan,贪心) 题面 洛谷 题解 如果图是一个\(DAG\),我们可以任意选择若干个不是入度为\(0\)的点,然后把它们按照拓扑序倒序删掉,不难证明这样一定是合法的. 现 ...
- Luogu P5008 逛庭院
题目传送门 我校神仙出的神仙题 \(\%\%\%\) 30分 找出所有有入度的点,排序,选前\(k\)个点,好了,30分到手. #include<iostream> #include< ...
- 【洛谷P5008 逛庭院】tarjan缩点+贪心
既然没有题解,那么我就来提供给一份. -- 首先我们看到数据范围.妈耶!数据这么大,一开始还想用个DP来做,但是看着就不行,那么根据这个数据范围,我们大致可以猜到这道题的算法是一个贪心,那么我们怎么贪 ...
- 水果姐逛水果街Ⅱ codevs 3305
3305 水果姐逛水果街Ⅱ 时间限制: 2 s 空间限制: 256000 KB 题目描述 Description 水果姐第二天心情也很不错,又来逛水果街. 突然,cgh又出现了.cgh施展了魔 ...
- codevs3305 水果姐逛水果街Ⅱ
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
- [vijos P1083] 小白逛公园
不知怎地竟有种错觉此题最近做过= =目测是类似的?那道题貌似是纯动归? 本来今晚想做两道题的,一道是本题,一道是P1653疯狂的方格取数或NOI08 Employee,看看现在的时间目测这个目标又达不 ...
- Bzoj 1756: Vijos1083 小白逛公园 线段树
1756: Vijos1083 小白逛公园 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1021 Solved: 326[Submit][Statu ...
- Codevs 3305 水果姐逛水果街Ⅱ 倍增LCA
题目:http://codevs.cn/problem/3305/ 时间限制: 2 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Des ...
随机推荐
- C# 简单的 Job 作业~
改变之前的前言,这次咱这样写: 一个习惯只需十天的坚持就可以养成,坏习惯也不例外!吸烟喝酒的我能否做到十天不吸烟喝酒呢? 呵呵 养成习惯关键还要看决心和意志力 恩,努力控烟吧! 废话说完了,就进入咱们 ...
- CF [2016-2017 ACM-ICPC CHINA-Final][GYM 101194 H] Great Cells
很久以前做的一道思博题了,今天来补一补. 大致题意:在一个\(n*m\)的矩阵内填整数,数字在\([1,k]\)范围内.矩阵中某格的数为great number当且仅当与它同行同列的数字都严格比它小. ...
- Luogu P1265 公路修建
一眼看去,就是一道MST的模板题. 然后果断准备跑Kruskal,然后5个TLE. Kruskal复杂度对于这个完全图要O(n^2*logn^2),快排就会导致超时. 然后打了刚学的Prim.朴素O( ...
- [Partition][Index]对于Partition表而言,是否Global Index 和 Local Index 可以针对同一个字段建立?
对于Partition表而言,是否Global Index 和 Local Index 可以针对同一个字段建立? 实验证明,对单独的列而言,要么建立 Global Index, 要么建立 Local ...
- html5录音支持pc和Android、ios部分浏览器,微信也是支持的,JavaScript getUserMedia
以前在前人基础上重复造了一个网页录音的轮子,顺带把github仓库使用研究了一下,扔到了github上. 优势在于结构简单,可插拔式的录音格式支持,几乎可以支持任意格式(前提有相应的编码器):默认提供 ...
- 事件(event)
事件概述 委托是一种类型可以被实例化,而事件可以看作将多播委托进行封装的一个对象成员(简化委托调用列表增加和删除方法)但并非特殊的委托,保护订阅互不影响. 基础事件(event) 在.Net中声明事件 ...
- SSO单点登录_理解
SSO核心意义就一句话:一处登录,处处登录:一处注销,处处注销.即:在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 很多人容易把SSO与OAuth搞混.这里简单说明一下: OA ...
- 求去掉一条边使最小割变小 HAOI2017 新型城市化
先求最小割,然后对残量网络跑Tarjan.对于所有满流的边,若其两端点不在同一个SCC中,则这条边是满足条件的. 证明见 来源:HAOI2017 新型城市化
- 使用git命令创建分支到团队项目
背景 在我们的团队中,我作为管理者,创建了一个叫HelloWorld的项目,大家各自在本地进行开发,将自己的工作贡献到我们的团队项目中.为了便于审核,我希望大家先将自己的贡献先放在属于自己的一个分支上 ...
- QT应用在windows和Linux平台的发布指南
环境:QT5.4 Windows下Qt应用的发布 Qt安装路径为:C:\Qt\Qt5.4.0\5.4\mingw491_32\bin 首先确保这个路径不在环境变量中,否则可能不成功. 执行" ...