【BZOJ1565】 植物大战僵尸
Description
Input
Output
仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。
Sample Input
3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
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=610,INF=2147000000; 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[1000010]; inline int min(int x,int y){return x<y?x:y;} inline int id(int x,int y){return (x-1)*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=0; g[tot].next=h[v]; h[v]=tot; } bool bfs(){ while(!q.empty()) q.pop(); q.push(S); for(int i=1;i<=T;i++) dis[i]=-1; dis[S]=0; 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]==-1){ dis[v]=dis[u]+1; if(v==T) return true; q.push(v); } } return dis[T]!=-1; } int dfs(int u,int delta){ if(u==T) return delta; int ret=0,get; for(int i=cur[u],v;i&δi=g[i].next) if(g[i].f&&dis[v=g[i].v]==dis[u]+1){ get=dfs(v,min(delta,g[i].f)); g[i].f-=get; g[i^1].f+=get; if(g[i].f) cur[u]=i; delta-=get; ret+=get; } if(!ret) dis[u]=-1; return ret; } int dinic(){ int ret=0; while(bfs()){ for(int i=1;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]=1; 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]=0; top--; return; } int x; do{ x=st[top--]; ins[x]=0; is[x]=1; }while(x!=u); } } void clear(int u){ vis[u]=1; is[u]=1; 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=1;i<=n;i++) for(int j=1;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+1,y+1),k); list[id(x+1,y+1)].push_back(k); } } for(int i=1;i<=n;i++) for(int j=1;j<m;j++) addEdge(id(i,j),id(i,j+1)); for(int i=1,up=n*m;i<=up;i++) if(!dfn[i]) tarjan(i,0); for(int i=1,up=n*m;i<=up;i++) if(!vis[i]&&is[i]) clear(i); for(int i=1,up=n*m;i<=up;i++) h[i]=0; tot=1; S=n*m+1; T=n*m+2; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ int x=id(i,j); if(j<m&&!is[x]&&!is[id(i,j+1)]) addEdge(x,id(i,j+1),INF); if(is[x]) continue; if(a[x]>=0){ addEdge(S,x,a[x]); sum+=a[x]; } else addEdge(x,T,-a[x]); int sz=list[x].size(); for(int k=0;k<sz;k++) addEdge(x,list[x][k],INF); } int maxflow=dinic(); printf("%d\n",sum-maxflow); return 0; }