P1262 间谍网络 (tarjan缩点 水过去)
题目描述
由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果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缩点 水过去)的更多相关文章
- Luogu P2002 消息扩散&&P1262 间谍网络
怕自己太久没写Tarjan了就会把这种神仙算法忘掉. 其实这种类型的图论题的套路还是比较简单且显然的. P2002 消息扩散 很显然的题目,因为在一个环(其实就是强连通分量)中的城市都只需要让其中一个 ...
- 洛谷——P1262 间谍网络
P1262 间谍网络 题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意 ...
- 洛谷 P1262 间谍网络 —— 缩点
题目:https://www.luogu.org/problemnew/show/P1262 首先,一个强连通分量里有一个点被控制则所有点都被控制,所以先 tarjan 缩点,记一下每个连通块中能被收 ...
- tyvj 1153 间谍网络 tarjan有向图强连通
P1153 - 间谍网络 From ForeverBell Normal (OI)总时限:13s 内存限制:128MB 代码长度限制:64KB 描述 Description 由于外国 ...
- P1262 间谍网络
传送门 思路: ①在 Tarjan 的基础上加一个 belong 记录每个点属于哪个强连通分量. ②存图完成后,暴力地遍历全图,查找是否要间谍不愿受贿. inline void dfs(int u) ...
- luogu P1262 间谍网络
嘟嘟嘟 建图还是很明显的. 接着分两种情况: 1.图中不存在环:那么只要收买那些入度为0的点.如果这些点有的不能收买.就不能控制所有间谍. 2.图中存在环,那么对于这些在环中的点,我们只要收买数额最少 ...
- 【luogu P1262 间谍网络】 题解
题目链接:https://www.luogu.org/problemnew/show/P1262 注意: 1.缩点时计算出入度是在缩完点的图上用color计算.不要在原来的点上计算. 2.枚举出入度时 ...
- 洛谷 P1262 间谍网络==Codevs 4093 EZ的间谍网络
4093 EZ的间谍网络 时间限制: 10 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B ...
- 洛谷 1262 间谍网络 Tarjan 图论
洛谷 1262 图论 tarjan 并不感觉把这道题目放在图的遍历中很合适,虽然思路比较简单但是代码还是有点多的,, 将可收买的间谍的cost值设为它的价格,不可购买的设为inf,按照控制关系连图,T ...
随机推荐
- HDU - 4578 Transformation(线段树区间修改)
https://cn.vjudge.net/problem/HDU-4578 题意 4种操作,区间加,区间乘,区间变为一个数,求区间的和.平方和以及立方和. 分析 明显线段树,不过很麻烦..看kuan ...
- svn各种表示含义及解决
- vue使用vue-awesome-swiper及一些问题
vue-awesome-swiper是基于swiper的一个轮播图插件,使用非常方便. 首先安装下 npm install vue-awesome-swiper --save 然后在入口文件main. ...
- 细说shiro之七:缓存
官网:https://shiro.apache.org/ 一. 概述 Shiro作为一个开源的权限框架,其组件化的设计思想使得开发者可以根据具体业务场景灵活地实现权限管理方案,权限粒度的控制非常方便. ...
- 细说shiro之二:组件架构
官网:https://shiro.apache.org/ Shiro主要组件包括:Subject,SecurityManager,Authenticator,Authorizer,SessionMan ...
- 对两个数求和的str_echo函数
void str_echo(int sockfd) { long arg1, arg2; ssize_t n; char line[MAXLINE]; for ( ; ; ) { ) { return ...
- Restful API学习Day5 - DRF之限制 分页 解析器和渲染器
参考文档: Django REST framework基础:认证.权限.限制 Django REST framework基础:分页 Django REST framework基础:解析器和渲染器 一. ...
- luogu P3162 [CQOI2012]组装
传送门 mdzz,为什么这题有个贪心的标签啊qwq 首先考虑每一种车间,对于每相邻两个车间,在中点左边那么左边那个会贡献答案,在右边就右边那个更优 所以总共会有m-1个这样的分界中点,然后最多有m+1 ...
- python之字符串常用的方法
1. 去掉空格或换行符 s='. hello .world .\n' new_s = s.strip()#默认去掉字符串前后的空格和换行符 new_s = s.strip('.')#可传参去掉字符串前 ...
- 多项式乘法(FFT)学习笔记
------------------------------------------本文只探讨多项式乘法(FFT)在信息学中的应用如有错误或不明欢迎指出或提问,在此不胜感激 多项式 1.系数表示法 ...