bzoj4823[CQOI2017]老C的方块

题意

不简述题意了,简述题意之后这题就做出来了.粘一发pdf好了.
https://files.cnblogs.com/files/liu-runda/CQOI2017day2.pdf

分析

首先我们发现题面很长,所以我们要好好读题.
特殊边的排列方式很规律,但是并没有什么一眼能看出来的优雅性质.我们要观察4种讨厌的形状,找出它们本质的共同点.
不难发现,特殊边两侧各有一个方格,且特殊边直接相连的两个方块又各自连着一个方块.也就是说,是两个大小为2的连通块通过一条特殊边相连得到的.
因此我们想到,每个特殊边两侧的方格在这道题中是比较重要的.首先,如果某个特殊边能够产生一个讨厌的形状,那么这条特殊边两侧的格子必须都存在.而只要删除两侧的格子其中的一个,就可以使得这条特殊边无法产生讨厌的形状.如果不删除这两侧的格子的其中一个,那么两侧的格子不能同时都与其他的格子相连.
这样说是比较抽象的,我们需要看图.做这道题的时候我把pdf给的图截下来,用画图上色,于是变成了这样.

红配绿,赛狗屁
考虑第2行第3,4列中两个绿色格子,它们位于一条特殊边两侧.如果现在这两个格子都存在,我们要么删掉它们中的一个,要么删掉左边的格子所连的全部蓝色格子,要么删掉右边的格子所连的全部红色格子.这好像有一些"不共存"的特征,如果选手对网络流的套路比较熟悉,现在应当往最小割上考虑.
考虑中间两个绿色格子.既然它们只需要删一个就可以保证不产生讨厌的形状,那么删的时候一定是删较小的那一个.
考虑两个绿色格子分别相连的红色格子蓝色格子,任取一个红色格子和一个蓝色格子,如果不删掉一个绿色格子,这两个格子就不能共存.
考虑问题的简化版本.如果绿色格子不允许删除,那么就变成了比较裸的最小割.按上面的方式染色,只有红色格子蓝色格子之间会不能共存,任意两个红色格子或者任意两个蓝色格子都可以共存.那么就变成了二分图的最小点割集问题,可以用最大流(即最小割)解决.从源点向每个红色格子连一条流量为删掉这个格子代价的边,从每个蓝色格子向汇点连一条流量为删掉这个格子代价的边,每个红色格子向相邻的绿色格子连容量正无穷的边,绿色格子向相邻的蓝色格子连容量正无穷的边.而对于相邻的两个绿色格子,一定是一个与红色格子相邻,一个与蓝色格子相邻,那么从与红色格子相邻的格子向与蓝色格子相邻的格子连正无穷的边即可.
如果允许删除绿色格子?我们只需要把绿色格子之间的边的权值从正无穷改成两个绿色格子的删除代价中较小的一个.之前这条边权值为正无穷,表示无论如何两侧红色与蓝色格子都不共存.现在如果删掉代价较小的绿色格子就可以使得两侧的格子共存,所以我们把绿色格子之间的边权改成两个绿色格子的代价中较小的,割掉这条边代表有一个绿色格子被删除,那么两侧的格子就可以共存了.
细节看代码.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<utility>
using namespace std;
map<pair<int,int>,int> dict;
typedef pair<int,int> pr;
const int maxn=100005,maxm=5000005;
struct edge{
  int to,next,w;
}lst[maxm];int len=0,first[maxn],_first[maxn];
void addedge(int a,int b,int w){
  lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;
  lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
int q[maxn],dis[maxn],vis[maxn],head,tail,T,s,t;
bool bfs(){
  head=tail=0;q[tail++]=s;vis[s]=++T;dis[s]=1;
  while(head!=tail){
    int x=q[head++];
    for(int pt=first[x];pt!=-1;pt=lst[pt].next){
      if(lst[pt].w&&vis[lst[pt].to]!=T){
	    vis[lst[pt].to]=T;dis[lst[pt].to]=dis[x]+1;q[tail++]=lst[pt].to;
      }
    }
  }
  if(vis[t]==T)memcpy(_first,first,sizeof(first));
  return vis[t]==T;
}
int dfs(int x,int lim){
  if(x==t)return lim;
  int flow=0,a;
  for(int pt=_first[x];pt!=-1;pt=lst[pt].next){
    _first[x]=pt;
    if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){
      lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;
      if(flow==lim)return flow;
    }
  }
  return flow;
}
int dinic(){
  int ans=0,x;
  while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;
  return ans;
}
int x[maxn],y[maxn],w[maxn];
bool isred(int a,int b){
  int typ=((a+1)/2)&1;
  if(typ==1){
    if(b%2==1)return false;
  }else{
    if(b%2==0)return false;
  }
  typ=(a/2)&1;
  return typ==0;
}
bool isblue(int a,int b){
  int typ=((a+1)/2)&1;
  if(typ==1){
    if(b%2==1)return false;
  }else{
    if(b%2==0)return false;
  }
  typ=(a/2)&1;
  return typ==1;
}
bool isgreen(int a,int b){
  int typ=((a+1)/2)&1;
  if(typ==1){
    if(b%2==1)return true;
  }else{
    if(b%2==0)return true;
  }
  return false;
}
int d1[3][2]={{0,1},{0,-1},{-1,0}},d2[3][2]={{0,1},{0,-1},{1,0}};
void addin(int p,int d[3][2]){
  int xx,yy;
  for(int i=0;i<3;++i){
    xx=x[p]+d[i][0];yy=y[p]+d[i][1];
    int t=dict[pr(xx,yy)];
    if(t)addedge(t,p,0x7f7f7f7f);
  }
}
void addout(int p,int d[3][2]){
  int xx,yy;
  for(int i=0;i<3;++i){
    xx=x[p]+d[i][0];yy=y[p]+d[i][1];
    int t=dict[pr(xx,yy)];
    if(t)addedge(p,t,0x7f7f7f7f);
  }
}
int main(){
  //  double t1=clock();
  freopen("block.in","r",stdin);
  freopen("block.out","w",stdout);
  memset(first,-1,sizeof(first));
  int n;scanf("%*d%*d%d",&n);s=0;t=n+1;
  for(int i=1;i<=n;++i){
    scanf("%d%d%d",x+i,y+i,w+i);
    if(isred(x[i],y[i])){
      addedge(s,i,w[i]);
    }else if(isblue(x[i],y[i])){
      addedge(i,t,w[i]);
    }
    dict[pr(x[i],y[i])]=i;
  }
  for(int i=1;i<=n;++i){
    if(x[i]%2==1&&isgreen(x[i],y[i])){
      if(dict[pr(x[i]+1,y[i])]!=0){
		int j=dict[pr(x[i]+1,y[i])];
		if(y[i]&1){//x[i]=1,5...
		  addedge(i,j,min(w[i],w[j]));
		  addin(i,d1);addout(j,d2);
		}else{
		  addedge(j,i,min(w[i],w[j]));
		  addin(j,d2);addout(i,d1);
		}
      }
    }
  }
  printf("%d\n",dinic());
  //  double t2=clock();
  //  printf("%.3fs\n",(t2-t1)/CLOCKS_PER_SEC);
  fclose(stdin);fclose(stdout);
  return 0;
}

posted @ 2017-04-11 19:45  liu_runda  阅读(511)  评论(3编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难