[补集转换] Topcoder SRM563 DIV1. CoinsGame

枚举两个点,如果经过一系列操作使得一个留在棋盘上一个不在,那么这个点对是有价值的

那么合法的放棋子的方案一定包含至少一个有价值的点对

但是点对个数是 O((nm)2) O ( ( n m ) 2 ) 的,直接容斥不行

补集转化一下,可以发现如果点对 (a,b) ( a , b ) 和点对 (b,c) ( b , c ) 是没有价值的,那么点对 (a,c) ( a , c ) 也是没有价值的

也就是没有价值的点对会形成若干个团。

答案就是所有方案减去每个团的方案

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <assert.h>
#include <queue>
#define fi first
#define se second

using namespace std;

typedef pair<int,int> pii;
typedef pair<pii,pii> piii;

const int N=50,P=1e9+9;

int n,m;
char a[N][N];

inline int gout(int x,int y){
  return x<1 || x>n || y<1 || y>m;
}

int fa[N*N],size[N*N],vis[N][N][N][N];

int getfa(int x){
  return x==fa[x]?x:fa[x]=getfa(fa[x]);
}

inline void Merge(int x,int y){
  x=getfa(x); y=getfa(y);
  if(x==y) return ;
  size[x]+=size[y]; fa[y]=x;
}

int tot[N*N],t;

inline int Pow(int x,int y){
  int ret=1;
  for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
  return ret;
}

vector<int> G;

inline int cb(int ax,int ay,int bx,int by,int _ax,int _ay,int _bx,int _by){
  for(int x=-1;x<=1;x++)
    for(int y=-1;y<=1;y++){
      if((x || y) && (!x || !y)){
    int anx=ax,any=ay,bnx=bx,bny=by;
    if(a[anx+x][any+y]!='#') anx+=x,any+=y;
    if(a[bnx+x][bny+y]!='#') bnx+=x,bny+=y;
    if(anx==_ax && any==_ay && bnx==_bx && bny==_by) return 1;
      }
    }
  return 0;
}

queue<piii> Q;

/*void dfs(int ax,int ay,int bx,int by){
  assert(!gout(ax,ay) && !gout(bx,by));
  if(vis[ax][ay][bx][by]) return ;
  vis[ax][ay][bx][by]=1;
#define ck(i,j,k,s) if(!gout(i,j) && !gout(k,s) && a[i][j]!='#' && a[k][s]!='#' && cb(i,j,k,s,ax,ay,bx,by)) dfs(i,j,k,s);
  ck(ax,ay,bx-1,by);
  ck(ax,ay,bx+1,by);
  ck(ax,ay,bx,by-1);
  ck(ax,ay,bx,by+1);
  ck(ax-1,ay,bx,by);
  ck(ax+1,ay,bx,by);
  ck(ax,ay+1,bx,by);
  ck(ax,ay-1,bx,by);
  ck(ax-1,ay,bx-1,by);
  ck(ax+1,ay,bx+1,by);
  ck(ax,ay-1,bx,by-1);
  ck(ax,ay+1,bx,by+1);
  }*/

inline void addedge(int y){
  G.push_back(y);
}

class CoinsGame{
public:
  int ways(vector<string> board){
    n=board.size(); m=board[0].size();
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
    a[i][j]=board[i-1][j-1],fa[(i-1)*m+j]=(i-1)*m+j,size[(i-1)*m+j]=1;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
    for(int k=1;k<=n;k++)
      for(int s=1;s<=m;s++)
        if((i!=k || j!=s) && a[i][j]!='#' && a[k][s]!='#'){
          for(int x=-1;x<=1;x++)
        for(int y=-1;y<=1;y++){
          if((x || y) && (!x || !y)){
            int anx=i,any=j,bnx=k,bny=s;
            if(a[anx+x][any+y]!='#') anx+=x,any+=y;
            if(a[bnx+x][bny+y]!='#') bnx+=x,bny+=y;
            if(anx!=i || any!=j || bnx!=k || bny!=s){
              if(gout(anx,any)^gout(bnx,bny)){
            addedge(((i-1)*m+j)*(n*m+1)+(k-1)*m+s);
              }
            }
          }
        }
        }
    for(int i : G){
      int anx=(i/(n*m+1)-1)/m+1,any=(i/(n*m+1)-1)%m+1;
      int bnx=(i%(n*m+1)-1)/m+1,bny=(i%(n*m+1)-1)%m+1;
      //dfs(anx,any,bnx,bny);
      Q.push({{anx,any},{bnx,bny}});
      vis[anx][any][bnx][bny]=1;
    }
    while(!Q.empty()){
      int ax=Q.front().fi.fi,ay=Q.front().fi.se;
      int bx=Q.front().se.fi,by=Q.front().se.se;
      Q.pop();
#define ck(i,j,k,s) if(!gout(i,j) && !gout(k,s) && !vis[i][j][k][s] && a[i][j]!='#' && a[k][s]!='#' && cb(i,j,k,s,ax,ay,bx,by)) Q.push({{i,j},{k,s}}),vis[i][j][k][s]=1;
      ck(ax,ay,bx-1,by);
      ck(ax,ay,bx+1,by);
      ck(ax,ay,bx,by-1);
      ck(ax,ay,bx,by+1);
      ck(ax-1,ay,bx,by);
      ck(ax+1,ay,bx,by);
      ck(ax,ay+1,bx,by);
      ck(ax,ay-1,bx,by);
      ck(ax-1,ay,bx-1,by);
      ck(ax+1,ay,bx+1,by);
      ck(ax,ay-1,bx,by-1);
      ck(ax,ay+1,bx,by+1);
    }

    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
    for(int k=1;k<=n;k++)
      for(int s=1;s<=m;s++)
        if((i!=k || j!=s) && a[i][j]!='#' && a[k][s]!='#' && !vis[i][j][k][s])
          Merge((i-1)*m+j,(k-1)*m+s);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
    if(a[i][j]!='#' && getfa((i-1)*m+j)==(i-1)*m+j)
      tot[++t]=size[(i-1)*m+j];
    int cnt=0;
    for(int i=1;i<=t;i++) cnt+=tot[i];
    int ans=Pow(2,cnt)-1;
    for(int i=1;i<=t;i++) ans=(ans-Pow(2,tot[i])+1)%P;
    return (ans+P)%P;
  }
}Main;

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  int n,m;
  scanf("%d%d",&n,&m);
  vector<string> b={{"....#....#..#.#.........#...#...........", "....#....#..#.#.........#...#.........##", "....#....#..#.#.........#...#........#..", "....#....#..#.#.........#...#........#..", "....#....#..#.#.........#...#.........##", "....######..###.........#####........#..", "......................................##", "........................................", "........................................", ".............................###########", "##...........................#..........", ".##..........................#..........", "..#..........................#..........", "..#..........................#..........", ".##........#######...........#..........", "##.........#.....#...........#..........", "...........#.....#...........###########", "...........#.....#......................", "##.........#######......................", ".##.....................................", "..#..........................###########", "..#..........................#..........", ".##..........................#..........", "##...........................#..........", ".................##..........#..........", "...............###.##........#..........", "..............#......##......###########", "............##..#...#..#................", ".............#....#....#.....###########", "#####.........##......#......#..........", "....#...........##.###.......#..........", "....#..###.......##..........###########", "#####..#.#..............................", "....#..#.###............................", "#####..#.#.#............................", "....#..#.#.#....##########..###.#####...", "#####..#.#.######..#.....#..#.#.#...#...", ".......#.#.#.#..#..#.....#..#.#.#...#...", ".......#.#.#.#..#..#.....#..#.#.#...#...", ".......#.#.#.#..#..#.....#..#.#.#...#..."}};
  printf("%d\n",Main.ways(b));
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值