题目描述

由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。

我们的反间谍机关提供了一份资料,色括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每个间谍分别用1到3000的整数来标识。

请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。

输入输出格式

输入格式:

第一行只有一个整数n。

第二行是整数p。表示愿意被收买的人数,1≤p≤n。

接下来的p行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过20000。

紧跟着一行只有一个整数r,1≤r≤8000。然后r行,每行两个正整数,表示数对(A, B),A间谍掌握B间谍的证据。

输出格式:

如果可以控制所有间谍,第一行输出YES,并在第二行输出所需要支付的贿金最小值。否则输出NO,并在第二行输出不能控制的间谍中,编号最小的间谍编号。

输入输出样例

输入样例#1:

3

2

1 10

2 100

2

1 3

2 3

输出样例#1:

YES

110

输入样例#2:

4

2

1 100

4 200

2

1 2

3 4

输出样例#2:

NO

3

说明

懒得多说直接上代码了。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0; char c=getchar();
while(!isdigit(c)) c=getchar();
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x;
}
int n,p,r,pat,top,tim,ans;
//pat表示边数,part表示强连通分量数,top表示栈高,tim表示时间戳
int mon[3100];
//money
int blg[3100],fa[3100];
//belong、father(其实两者本质是一样的,关于father等会儿会讲)
int dfn[3100],low[3100];
//深度优先(英文)、lowest(应该是吧)
int stk[3100],head[3100];
//stack(栈)、head是邻接链表的头(其实我觉得更像是尾)
bool instk[3100];
//instack表示是否在栈里面
struct tpat
{
int v,next;
}edge[8100]; void add(int u,int v)
{
edge[pat].next=head[u];
edge[pat].v=v;
head[u]=pat++;
} //邻接表存边( 比向量vector的push_back()要快 ) void tarjan(int u) //tarjan缩点不详解,自行百度
{
dfn[u]=low[u]=++tim;
instk[u]=true; stk[++top]=u;
for(int i=head[u];i!=-1;i=edge[i].next) //遍历点u的邻接表
{
int v=edge[i].v;
if(!dfn[v])
tarjan(v),low[u]=min(low[u] , low[v]);
else if(instk[v]) //这点很重要!被搜到过但不在栈内的话就不能操作
low[u]=min(low[u] , dfn[v]);
}
if(dfn[u]==low[u]) //low无变化代表u是一个强连通分量的出发点
{
int j;
do{
j=stk[top--];//从栈顶一直弹弹弹(弹走鱼尾纹),弹到u点(当前点)
mon[u]=min(mon[u] , mon[j]);
instk[j]=false; //出栈
blg[j]=u;
//注意这里就是把belong[j]赋值为u了,方便缩点后的操作
}while(j!=u); //弹到当前节点就退出
}
} void solve()
{
for(int i=1;i<=n;++i) //fa[i]都赋为本身
fa[i]=i;
for(int i=1;i<=n;++i)
for(int j=head[i];j!=-1;j=edge[j].next)
if(blg[i]!=blg[edge[j].v]) fa[blg[edge[j].v]]=blg[i];
//这里是不能把fa换成blg的,举个例子吧,
//如果只有三个点,然后有两条边使两个不同的点分别指向同一个点的话...
//想必你也猜到了,我也栽在这儿过~~~ _(:з」∠)_
for(int i=1;i<=n;++i)
{
if(blg[i]!=i || fa[i]!=i) //不是必须要付费的间谍就continue
continue;
if(mon[i]==0x3f3f3f3f) //如果说这个点无穷大就直接输出+退出
//话说我怎么觉得这里是有bug的来着?貌似我自己都能把自己hack掉...
//额...不过洛谷上是水过去了... _(:з」∠)_ 下文再放个图试试?
{
printf("NO\n%d\n",i);
return ; //输完直接退
}
ans+=mon[i]; //否则答案累加
}
printf("YES\n%d\n",ans);
} int main()
{
n=read();p=read();
memset(mon,0x3f,sizeof(mon));
memset(head,-1,sizeof(head));
for(int i=0;i<p;++i)
mon[read()]=read();
r=read();
for(int i=0;i<r;++i)
{ int u=read(),v=read(); add(u,v); } for(int i=1;i<=n;++i) //缩点
if(!dfn[i])
tarjan(i); solve(); //缩点完开始正式工作
return 0;
}

(看我注解写的多认真,你能忍心不点个赞吗? _ (:з」∠)_ )

然后我来说说我是怎么hack掉自己的程序的 … -_-|| (方便起见,假设没有一个间谍能被收买)

然后这就hack了?洛谷上还水过去了?心中一万匹草泥马在奔腾

然后呢?标程在哪里?

然我先想想…emmm好了上代码吧!

其实…只要把solve函数改成这样就好了:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0; char c=getchar();
while(!isdigit(c)) c=getchar();
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x;
}
int n,p,r,pat,top,tim,ans;
int mon[3100],mnode[3100];
//多开了一个mnode表示:如果该点是其所在的强连通分量的出发点,则该强连通分量中最小的点的编号
int blg[3100],fa[3100];
int dfn[3100],low[3100];
int stk[3100],head[3100];
bool instk[3100];
struct tpat
{
int v,next;
}edge[8100]; void add(int u,int v)
{
edge[pat].next=head[u];
edge[pat].v=v;
head[u]=pat++;
} void tarjan(int u)
{
dfn[u]=low[u]=++tim;
instk[u]=true; stk[++top]=u;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!dfn[v])
tarjan(v),low[u]=min(low[u] , low[v]);
else if(instk[v])
low[u]=min(low[u] , dfn[v]);
}
if(dfn[u]==low[u]) //low无变化代表u是一个强连通分量的出发点
{
int j;
do{
j=stk[top--];//从栈顶一直弹弹弹(弹走鱼尾纹),弹到u点(当前点)
mon[u]=min(mon[u] , mon[j]);
instk[j]=false; //出栈
blg[j]=u;
if(mnode[j]==4000 || mnode[u]==4000)
//如果强连通分量中有人可以收买,则整个强连通分量中的人都可以被控制
mnode[u]=4000;
else mnode[u]=min(mnode[u] , mnode[j]);
}while(j!=u);
}
} void solve()
{
for(int i=1;i<=n;++i)
for(int j=head[i];j!=-1;j=edge[j].next)
if(blg[i]!=blg[edge[j].v])
fa[blg[edge[j].v]]=blg[i]; for(int i=1;i<=n;++i)
{
if(blg[i]==i)
mnode[fa[i]]=min(mnode[fa[i]] , mnode[i]);
//这里的意思是在某个强连通分量的出发点时将改点前驱的强连通分量的出发点取编号为min的操作
if(blg[i]!=i || fa[i]!=i)
continue;
if(mon[i]==0x3f3f3f3f)
{
printf("NO\n%d\n",mnode[i]); //然后这里输出就变成了当前点的最小编号
return ;
}
ans+=mon[i];
}
printf("YES\n%d\n",ans);
} int main()
{
n=read();p=read();
memset(mon,0x3f,sizeof(mon));
memset(head,-1,sizeof(head));
for(int i=0;i<p;++i)
mon[read()]=read();
r=read();
for(int i=0;i<r;++i)
{ int u=read(),v=read(); add(u,v); }
for(int i=1;i<=n;++i)
{ mnode[i]=fa[i]=i ; if(mon[i]!=0x3f3f3f3f) mnode[i]=4000; }
//可以收买的就直接为最大4000 for(int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i); solve();
return 0;
}

珍惜无语,洛谷数据是真水啊,圣水!(再吐槽一波)

OK,本篇blog的正文到此结束,然后我是无力吐槽了,累得要死 _ (:з」∠) _

各位看官们下次见!

P1262 间谍网络 (tarjan缩点 水过去)的更多相关文章

  1. Luogu P2002 消息扩散&&P1262 间谍网络

    怕自己太久没写Tarjan了就会把这种神仙算法忘掉. 其实这种类型的图论题的套路还是比较简单且显然的. P2002 消息扩散 很显然的题目,因为在一个环(其实就是强连通分量)中的城市都只需要让其中一个 ...

  2. 洛谷——P1262 间谍网络

    P1262 间谍网络 题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意 ...

  3. 洛谷 P1262 间谍网络 —— 缩点

    题目:https://www.luogu.org/problemnew/show/P1262 首先,一个强连通分量里有一个点被控制则所有点都被控制,所以先 tarjan 缩点,记一下每个连通块中能被收 ...

  4. tyvj 1153 间谍网络 tarjan有向图强连通

    P1153 - 间谍网络 From ForeverBell    Normal (OI)总时限:13s    内存限制:128MB    代码长度限制:64KB 描述 Description 由于外国 ...

  5. P1262 间谍网络

    传送门 思路: ①在 Tarjan 的基础上加一个 belong 记录每个点属于哪个强连通分量. ②存图完成后,暴力地遍历全图,查找是否要间谍不愿受贿. inline void dfs(int u) ...

  6. luogu P1262 间谍网络

    嘟嘟嘟 建图还是很明显的. 接着分两种情况: 1.图中不存在环:那么只要收买那些入度为0的点.如果这些点有的不能收买.就不能控制所有间谍. 2.图中存在环,那么对于这些在环中的点,我们只要收买数额最少 ...

  7. 【luogu P1262 间谍网络】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1262 注意: 1.缩点时计算出入度是在缩完点的图上用color计算.不要在原来的点上计算. 2.枚举出入度时 ...

  8. 洛谷 P1262 间谍网络==Codevs 4093 EZ的间谍网络

    4093 EZ的间谍网络 时间限制: 10 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B ...

  9. 洛谷 1262 间谍网络 Tarjan 图论

    洛谷 1262 图论 tarjan 并不感觉把这道题目放在图的遍历中很合适,虽然思路比较简单但是代码还是有点多的,, 将可收买的间谍的cost值设为它的价格,不可购买的设为inf,按照控制关系连图,T ...

随机推荐

  1. C#设计模式(9)——代理模式

    1.代理模式介绍 在软件开发中有时会遇到不能直接使用对象的问题,如我们要使用的对象在进程外,甚至在远程的机器上,但是我们要使用这个对象的功能怎么办呢?代理模式就可以用来解决这个问题.举一个生活中的例子 ...

  2. C#与 微信小程序 互为加解密方案

    CryptoJS下载地址: https://code.google.com/archive/p/crypto-js/downloads http://download.csdn.net/detail/ ...

  3. Mastering Markdown

    What is markdown? Markdown is a lightweight and easy-to-use syntax for styling all forms writing on ...

  4. JMETER压力测试报错:JAVA.NET.BINDEXCEPTION: ADDRESS ALREADY IN USE: CONNECT

    最近在实现接口压力测试的时候遇到这样的一个问题 当线程数持续上升到一个点的时候,运行脚本的时候有很多报错,如图: java.net.BindException: Address already in ...

  5. cxf与spring的整合

    cxf与spring的整合: 一:服务端相关配置(配置好后启动tomocat就自动发布了接口,浏览器打开验证下) 1:导入cxf以及spring的相关jar包; 2:在web.xml中增加配置: 代码 ...

  6. Spark整合HBase,Hive

    背景: 场景需求1:使用spark直接读取HBASE表 场景需求2:使用spark直接读取HIVE表 场景需求3:使用spark读取HBASE在Hive的外表 摘要: 1.背景 2.提交脚本 内容 场 ...

  7. AndroidStudio替换空行

    (1)在Edit Replace In Path输入框中输入:^\s*\n (\s代表任何空白字符,\S代表任何非空白字符,*代表任意个数,\n匹配换行符) (2)Replace With输入框的值为 ...

  8. SQL Server 调用 C# 方法实现正则表达式验证

    Ø  前言 1.   在 SQL Server 中默认是不支持正则表达式验证的,如果需要某个字符串匹配一个正则表达式的验证规则,就需要额外的编写 C# 方法,并发布到 SQL Server 数据库中. ...

  9. 12.scrapy框架

    一.Scrapy 框架简介 1.简介 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个 ...

  10. Nginx正反向代理、负载均衡等功能实现配置

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   系统环境: VirtualBox Manager Centos6.4 nginx1.10.0 IP对应的机器名: IP ...