Description

Input

Output

  仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。

Sample Input

  3 2
  10 0
  20 0
  -10 0
  -5 1 0 0
  100 1 2 1
  100 0

Sample Output

  25
 

Solution

  按照依赖关系建立有向图:若清除$a$前必须清除$b$,则连边$a$至$b$。这些依赖关系包括同行植物左边对右边的依赖,以及被保护者对保护者的依赖。

  首先不能考虑开挂集团,也就是依赖关系成强联通分量的植物,也包括能通过依赖关系走到强联通分量的植物——它们都无敌,需要排除掉。这可以用tarjan加上反向边的深搜预处理。

  

  一个植物能够下手,当且仅当其出度为0.

  每一株植物又是带正负权值的,那么明显是要在依赖图中,求一个最大权闭合子图。也就是要有植物起手(迎合闭合的性质,出边都在闭合子图内,即依赖的植物都要一起干掉,都应该在闭合子图的范围内),且干掉的植物权值和最大。

  按照最大权闭合子图的建立方法:源点向所有正权点连容量为其权值的边,所有负权点向汇点连容量为其权值绝对值的边;所有节点按依赖关系连边,容量为$+\infty$。

  答案是所有正权点点权值和减去最大流。能将负权转化为正权来跑网络流的原因,拆拆括号就可以发现,这是很巧妙的。


#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int N=,INF=;
int n,m,a[N],h[N],hr[N],tot;
int dfn[N],low[N],tmcnt,is[N],st[N],top,ins[N],vis[N];
int S,T,dis[N],cur[N];
int sum;
queue<int> q;
vector<int> list[N];
struct Edge{int v,f,next;}g[];
inline int min(int x,int y){return x<y?x:y;}
inline int id(int x,int y){return (x-)*m+y;}
inline void addEdge(int u,int v){
g[++tot].v=v; g[tot].next=h[u]; h[u]=tot;
g[++tot].v=u; g[tot].next=hr[v]; hr[v]=tot;
}
inline void addEdge(int u,int v,int f){
g[++tot].v=v; g[tot].f=f; g[tot].next=h[u]; h[u]=tot;
g[++tot].v=u; g[tot].f=; g[tot].next=h[v]; h[v]=tot;
}
bool bfs(){
while(!q.empty()) q.pop();
q.push(S);
for(int i=;i<=T;i++) dis[i]=-;
dis[S]=;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=h[u],v;i;i=g[i].next)
if(g[i].f&&dis[v=g[i].v]==-){
dis[v]=dis[u]+;
if(v==T) return true;
q.push(v);
}
}
return dis[T]!=-;
}
int dfs(int u,int delta){
if(u==T) return delta;
int ret=,get;
for(int i=cur[u],v;i&&delta;i=g[i].next)
if(g[i].f&&dis[v=g[i].v]==dis[u]+){
get=dfs(v,min(delta,g[i].f));
g[i].f-=get; g[i^].f+=get;
if(g[i].f) cur[u]=i;
delta-=get; ret+=get;
}
if(!ret) dis[u]=-;
return ret;
}
int dinic(){
int ret=;
while(bfs()){
for(int i=;i<=T;i++) cur[i]=h[i];
ret+=dfs(S,INF);
}
return ret;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++tmcnt;
st[++top]=u; ins[u]=;
for(int i=h[u],v;i;i=g[i].next){
v=g[i].v;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(ins[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
if(st[top]==u){
ins[u]=;
top--;
return;
}
int x;
do{
x=st[top--];
ins[x]=;
is[x]=;
}while(x!=u);
}
}
void clear(int u){
vis[u]=; is[u]=;
for(int i=hr[u],v;i;i=g[i].next)
if(!vis[v=g[i].v])
clear(v);
}
int main(){
freopen("input.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
int k=id(i,j),l,x,y;
scanf("%d%d",&a[k],&l);
while(l--){
scanf("%d%d",&x,&y);
addEdge(id(x+,y+),k);
list[id(x+,y+)].push_back(k);
}
}
for(int i=;i<=n;i++)
for(int j=;j<m;j++)
addEdge(id(i,j),id(i,j+));
for(int i=,up=n*m;i<=up;i++)
if(!dfn[i])
tarjan(i,);
for(int i=,up=n*m;i<=up;i++)
if(!vis[i]&&is[i])
clear(i);
for(int i=,up=n*m;i<=up;i++) h[i]=;
tot=;
S=n*m+; T=n*m+;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
int x=id(i,j);
if(j<m&&!is[x]&&!is[id(i,j+)])
addEdge(x,id(i,j+),INF);
if(is[x]) continue;
if(a[x]>=){
addEdge(S,x,a[x]);
sum+=a[x];
}
else addEdge(x,T,-a[x]);
int sz=list[x].size();
for(int k=;k<sz;k++)
addEdge(x,list[x][k],INF);
}
int maxflow=dinic();
printf("%d\n",sum-maxflow);
return ;
}

奇妙代码

  

【BZOJ1565】 植物大战僵尸的更多相关文章

  1. BZOJ1565 植物大战僵尸

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1565 这题看上去并不会做,结果又是最大权闭合子图的裸题. 于是就去看了一发论文,明白建图的 ...

  2. [Android] 转移“植物大战僵尸2”存档的办法,无需root

    作者:zyl910 查过了很多文章,都说需要root后才能转移存档.但很多时候是不能root的,此时该怎么办呢? 我研究了很久,最终找到了一种办法,无需root也能转移存档. 一.备份 我用的是联想手 ...

  3. 32位汇编第六讲,OllyDbg逆向植物大战僵尸,快速定位阳光基址

    32位汇编第六讲,OllyDbg逆向植物大战僵尸,快速定位阳光基址 一丶基址,随机基址的理解 首先,全局变量的地址,我们都知道是固定的,是在PE文件中有保存的 但是高版本有了随机基址,那么要怎么解决这 ...

  4. 原生JS实现的h5小游戏-植物大战僵尸

    代码地址如下:http://www.demodashi.com/demo/12755.html 项目介绍 本项目是利用原生js实现的h5小游戏-植物大战僵尸,主要结合了一下自己对于h5小游戏的理解,结 ...

  5. 植物大战僵尸:寻找阳光掉落Call调用

    实验目标:通过遍历阳光产生的时间,寻找阳光产生的本地Call,使用代码注入器注入,自定义生成阳光 阳光CALL遍历技巧: 进入植物大战僵尸-> 当出现阳光后->马上搜索未知初始数值 返回游 ...

  6. java小项目之:植物大战僵尸,这个僵尸有点冷!内附素材源码

    Java小项目之:植物大战僵尸! <植物大战僵尸>是由PopCap Games开发的一款益智策略类单机游戏,于2009年5月5日发售,这款游戏可谓是无人不知无人不晓. 在我身边,上到40岁 ...

  7. 【bzoj1565】 NOI2009—植物大战僵尸

    http://www.lydsy.com/JudgeOnline/problem.php?id=1565 (题目链接) 题意 给出$n*m$的棋盘,僵尸攻击每个格子可以获得$v$的分数,每个格子又会保 ...

  8. 【最大权闭合子图 tarjan】bzoj1565: [NOI2009]植物大战僵尸

    dinic+tarjan板子练手题 Description Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其 中P ...

  9. BZOJ1565: [NOI2009]植物大战僵尸

    Description Input Output 仅包含一个整数,表示可以获得的最大能源收入.注意,你也可以选择不进行任何攻击,这样能源收入为0. Sample Input 3 2 10 0 20 0 ...

随机推荐

  1. Eclipse设置代码自动提示

    Eclipse只需几步简单的设置就可以像idea那样代码自动提示了,喜欢的小伙伴可以赶紧动手设置,提升效率. 第一步:打开Eclipse --> Window --> Preference ...

  2. css img换行之后有空隙

    这样的2个图片换行之后有空隙<img src="img/qiche.jpg" /> <br /> <img src="img/qiche.j ...

  3. Tomcat配置(三):tomcat处理连接的详细过程

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  4. 【转】SED多行模式空间

    在前面看到的都是单行模式.每次sed处理一个行. 但是sed是允许一次处理多行的.这就是所谓的多行模式空间. 多行模式空间命令有(N.D.P),他们分别对应单行模式空间(n.d.p). 分别是他们的多 ...

  5. Java对List进行分页

    Java对组装的List分页 以前一直是在DAO层直接从数据库里分页,但是今天因为有些数据,需要混合展示,就是根据条件取出了多个对象的集合,然后把这些多个List放到一个List里,然后在从这个Lis ...

  6. C++——函数重载

    C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载,方便使用,便于记忆. /*形参类型不同*/ int add(int x,int y); float add(float x,fl ...

  7. ABP官方文档翻译 3.6 工作单元

    工作单元 介绍 ABP中的连接和事务管理 传统的工作单元方法 控制工作单元 UnitOfWork特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 无事务工作单元 一个工作单元方法 ...

  8. AndroidStudio3更改包名失败

    使用Android Studio 3.0 Beta6更改包名refactor---rename一直提示:Refactoring cannot be performedFile xxx\build\xx ...

  9. BZOJ 1072: [SCOI2007]排列perm [DP 状压 排列组合]

    题意:给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0) 100%的数据满足:s的长度不超过10, 1<=d<=1000, 1<=T<=15 看到整 ...

  10. Python tutorial阅读之函数的定义与使用

    函数的定义 Python 使用关键字def定义函数,格式与C语言类似,但是没有返回类型,参数也不需要设置类型. def add(a, b): """这是函数的文档字符串& ...