【bzoj1565】 NOI2009—植物大战僵尸

http://www.lydsy.com/JudgeOnline/problem.php?id=1565 (题目链接)

题意

  给出$n*m$的棋盘,僵尸攻击每个格子可以获得$v$的分数,每个格子又会保护一些别的格子,想攻击被保护的格子必须先攻击没被保护的格子。问获得的最大分数。

Solution

  建图,被保护的点向保护它的点连边。显然,环上的点是不可能被击败的,我们Tarjan以后把环上的点以及指向环的点扔掉。剩下的点做最大权闭合子图就好了。

  这里扔掉不是真的扔掉,只要不让S和T与之相连就好了。

细节

  数组大小

代码

// bzoj1565
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf (1ll<<30)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;

const int maxn=100010,maxm=1000010;
int head[maxn],d[maxn],n,m,cnt,ans,tot,S,T;
int size[maxn],dfn[maxn],low[maxn],bel[maxn],vis[maxn],val[maxn],st[maxn],DFN,scc,top;
struct edge {int to,next,w;}e[maxm];
pair<int,int> E[maxm];

namespace Dinic {
	void link(int u,int v,int w) {
		e[++cnt]=(edge){v,head[u],w};head[u]=cnt;
		e[++cnt]=(edge){u,head[v],0};head[v]=cnt;
	}
	bool bfs() {
		for (int i=S;i<=T;i++) d[i]=-1;
		queue<int> q;q.push(S);d[S]=0;
		while (!q.empty()) {
			int x=q.front();q.pop();
			for (int i=head[x];i;i=e[i].next)
				if (e[i].w && d[e[i].to]<0) d[e[i].to]=d[x]+1,q.push(e[i].to);
		}
		return d[T]>0;
	}
	int dfs(int x,int f) {
		if (x==T || f==0) return f;
		int w,used=0;
		for (int i=head[x];i;i=e[i].next) if (e[i].w && d[e[i].to]==d[x]+1) {
				w=dfs(e[i].to,min(e[i].w,f-used));
				used+=w,e[i].w-=w;e[i^1].w+=w;
				if (used==f) return used;
			}
		if (!used) d[x]=-1;
		return used;
	}
	int main() {
		int flow=0;
		while (bfs()) flow+=dfs(S,inf);
		return flow;
	}
}

void link(int u,int v) {e[++cnt]=(edge){v,head[u]};head[u]=cnt;}
void Tarjan(int x) {
	st[++top]=x;dfn[x]=low[x]=++DFN;
	for (int i=head[x];i;i=e[i].next) {
		if (!dfn[e[i].to]) {
			Tarjan(e[i].to);
			low[x]=min(low[x],low[e[i].to]);
		}
		else if (!bel[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
	}
	if (dfn[x]==low[x]) {
		scc++;
		for (;st[top]!=x;top--) bel[st[top]]=scc,size[scc]++;
		bel[st[top--]]=scc;size[scc]++;
	}
}
bool dfs(int x) {
	if (vis[x]) return size[x];
	if (size[x]>1) size[x]=0;
	vis[x]=1;
	for (int i=head[x];i;i=e[i].next) if (e[i].w) size[x]&=dfs(e[i].to);
	return size[x];
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i=0;i<n;i++)
		for (int x,y,t,j=1;j<=m;j++) {
			scanf("%d%d",&val[i*m+j],&t);
			for (int k=1;k<=t;k++) scanf("%d%d",&x,&y),link(x*m+y+1,i*m+j);
			if (j<m) link(i*m+j,i*m+j+1);
		}
	for (int i=1;i<=n*m;i++) if (!dfn[i]) Tarjan(i);
	for (int i=1;i<=n*m;i++)
		for (int j=head[i];j;j=e[j].next)
			if (bel[i]!=bel[e[j].to]) E[++tot]=pair<int,int>(bel[i],bel[e[j].to]);
	memset(head,0,sizeof(head));cnt=1;
	for (int i=1;i<=tot;i++) Dinic::link(E[i].first,E[i].second,inf);
	for (int i=1;i<=scc;i++) if (!vis[i]) dfs(i);
	S=0,T=n*m+1;
	for (int i=1;i<=n*m;i++) if (size[bel[i]]) {
			if (val[i]>=0) Dinic::link(S,bel[i],val[i]),ans+=val[i];
			else Dinic::link(bel[i],T,-val[i]);
		}
	ans=max(0,ans-Dinic::main());
	printf("%d",ans);
	return 0;
}

 

posted @ 2017-03-26 10:44  MashiroSky  阅读(268)  评论(0编辑  收藏  举报