描述
蔡老板得到了一块 N * N 的方格,我们用 (x,y) 表示里面第 x 行第 y 列的房子 (1<=x,y<=N)。

现在蔡老板要在上面盖 N 间房子,盖房子要满足以下几个条件:

1.任意一行至少有一间房子

2.任意一列至少有一间房子

3.正对角线上 (即所有满足 x=y的格子)至少有一间房子

4.负对角线上 (即所有满足 x+y=N+1 的格子)至少有一间房子

不幸的是,刚好有 K 个方格因为种种原因不能在上面盖房子,现在蔡老板想知道有几种盖房子的方案

由于方案数可能过大,你只需要输出答案对 10007 取模的值即可

输入格式
第一行两个整数 N,K

接下来 K 行,每行两个正整数 x,y 表示 (x,y) 这个格子不能在上面盖房子

保证一个格子不会重复给多次

输出格式
输出一个非负整数,表示答案对 10007取模后的值

样例1
input
2 0
output
0
样例2
input
3 1
1 2
output
2
样例3
input
16 2
1 3
5 7
output
7970
数据范围
对于 10% 的数据,有 1≤N≤8
对于 30% 的数据,有 1≤N≤16
另有 20%的数据,满足K=0
对于 100% 的数据,有 1≤N≤32 , 0≤K≤8
限制
时间限制:1s

空间限制:512MB


大容斥…
部分分可以状压dp
但是这里行列之间的关系仅仅是每行每列只能放一个,对角线只能放一个
这样的话大可不必状压dp,浪费时间空间
同时还有一些地方不能盖房子

现在有四个限制:
①k个位置不能放
②每行每列至少放一个
③正负对角线至少放一个
④恰好放n个
显然对于④,告诉我们每行每列至少放且至多放①个
那么限制④就被去掉了

对于限制①,我们可以2k2k2^k进行容斥(也就是强制某些位置必须放)
假设iii为我们枚举的状态,numi" role="presentation" style="position: relative;">numinuminum_i表示iii中1" role="presentation" style="position: relative;">111的个数,ansiansians_i,对于iii的答案
ans=∑i=12k−1(−1)numi∗ansi" role="presentation" style="position: relative;">ans=∑2k−1i=1(−1)numi∗ansians=∑i=12k−1(−1)numi∗ansians = \sum_{i=1}^{2^k-1} (-1)^{num_i} * ans_i

考虑ansiansians_i怎么求
考虑限制③
我们再进行容斥
分别求出总方案数aaa
正对角线不放东西的方案数b" role="presentation" style="position: relative;">bbb
负对角线不放东西的方案数ccc
正负对角线都不放东西的方案数d" role="presentation" style="position: relative;">ddd
ansiansians_i就是这种情况下的a−b−c+da−b−c+da-b-c+d
然后限制③就没了

对于限制②
我们把之前强制放的位置对应的行列删除
剩下的就是一个更小的方阵,上面有一些禁区不能盖房子
原本的禁区就是两条对角线
删除一些位置之后我们可以大概想象出新禁区的大致图像
大致就是“跳跃式”的格子
难处理的是正负对角线都有禁区的情况
我们进行行列交换,使正负对角线的禁区大致都移到正对角线上,构成了一个从坐上到右下,要么是1∗11∗11*1要么是2∗22∗22*2的小方块组成的对角线

可能这一步有点难理解
起始我们可以对一开始的图就进行行列交换
把iii行与n−i+1" role="presentation" style="position: relative;">n−i+1n−i+1n-i+1行并在一起
列也是同样的处理这样就形成了这样一个图:

然后我们重复上述的操作之后
最后会剩下类似上图的方阵
那些画出来的是禁区
我们考虑在禁区的限制下我们怎么求方案

也是容斥,我们可以算出来放在禁区的方案数
我们用fifif_i表示在禁区盖了iii个房子,其它位置随意的方案数
ans=" role="presentation" style="position: relative;">ans=ans=ans=总方案−∑mi=1(−1)i∗fi−∑i=1m(−1)i∗fi-\sum_{i=1}^{m} (-1)^i *f_i
fifif_i可以用背包求出

那么这个问题就解决了。
枚举一些必须要放的位置2k2k2^k,对角线容斥4(O(1))4(O(1))4(O(1)),背包n2n2n^2
总复杂度O(2k∗4∗n2)O(2k∗4∗n2)O(2^k*4*n^2)
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
const int p = 1e4+7;
int n , k;
int Map[40][40] , num[500] , tmp[40][40];
int now[40][40] , C[40][40] , jc[40];
int dp[40];
bool flag_x[40] , flag_y[40];
struct point{int x , y;
}a[10];
struct fangkuai{int x1,y1,x2,y2;int f[5];
}fk[50];
/*
正对角线为2,负对角线为3,交叉为4
Map:原图
num:1~2^k-1中1的个数
tmp:删除行列后的图
now:容斥完对角线的图
flag:行列标记
fk:每一个方块
*/
int read()
{int sum = 0;char c = getchar();bool flag = true;while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();if(flag)  return sum;else return -sum;
}
int solve2(int n,int m)
{int y = 1;int tot = 0;while(y <= m){bool flag = false;rep(x,1,n)if(now[x][y] == 1){fk[++tot].x1 = x;fk[tot].y1 = y;if(fk[tot].x1 == n) fk[tot].x2 = n;elserep(i,x,n) if(now[i][y] == 1)fk[tot].x2 = i;else break;if(fk[tot].y1 == m) fk[tot].y2 = m;elserep(i,y,m)if(now[x][i] == 1)fk[tot].y2 = i;else break;int size = (fk[tot].y2 - fk[tot].y1+1) * (fk[tot].x2 - fk[tot].x1+1);fk[tot].f[0] = 1;fk[tot].f[2] = 0;fk[tot].f[3] = 0;fk[tot].f[4] = 0;if(size == 1)fk[tot].f[1] = 1;else if(size == 2)fk[tot].f[1] = 2;else if(size == 4)fk[tot].f[1] = 4,fk[tot].f[2] = 2;flag = true;break;}if(flag)y = fk[tot].y2 + 1;else y++;}ll ans = 0;memset(dp,0,sizeof(dp));dp[0] = 1;int now_xxx = min(n,m);rep(i,1,tot){repp(j,now_xxx,1){ dp[j] = (dp[j] + dp[j - 1] * fk[i].f[1]) % p;if(j >= 2) dp[j] = (dp[j] + dp[j - 2] * fk[i].f[2]) % p;if(j >= 4) dp[j] = (dp[j] + dp[j - 4] * fk[i].f[4]) % p;}}int flag = 1;rep(i,1,now_xxx){ans += (1ll * flag * dp[i] * jc[now_xxx - i]) % p;flag = -flag;ans %= p;}ans = (jc[now_xxx] - ans + p) % p;return ans;
}
int tmpx[40],tmpy[40];
int solve1(int x)
{int tot = 1;memset(flag_x,0,sizeof(flag_x));memset(flag_y,0,sizeof(flag_y));int noww = x;bool flag1 = false , flag2 = false;while(noww){if(noww & 1){int xxx = a[tot].x , yyy = a[tot].y;if(flag_x[xxx]) return 0;if(flag_y[yyy]) return 0;flag_x[xxx] = true;flag_y[yyy] = true;if(Map[xxx][yyy] == 12 || Map[xxx][yyy] == 14) flag1 = true;if(Map[xxx][yyy] == 13 || Map[xxx][yyy] == 14) flag2 = true;}noww /= 2;tot++;}int totx = 0,toty = 0;rep(i,1,n) if(!flag_x[i]) tmpx[++totx] = i;rep(i,1,n) if(!flag_y[i]) tmpy[++toty] = i;rep(i,1,totx)rep(j,1,toty)tmp[i][j] = Map[tmpx[i]][tmpy[j]];int ans = 1ll * C[totx][n-num[x]] * C[toty][n-num[x]] % p;ans = ans * jc[n-num[x]] % p;rep(i,1,totx)//正对角线rep(j,1,toty)if(tmp[i][j]%10 == 2 || tmp[i][j]%10 == 4)now[i][j] = 1;else now[i][j] = 0;if(!flag1)ans = (ans - solve2(totx,toty) + p)%p;rep(i,1,totx)rep(j,1,toty)if(tmp[i][j]%10 == 3 || tmp[i][j]%10 == 4)now[i][j] = 1;else now[i][j] = 0;if(!flag2)ans = (ans - solve2(totx,toty) + p)%p;rep(i,1,totx) rep(j,1,toty)if(tmp[i][j] == 0 || tmp[i][j] == 1)now[i][j] = 0;else now[i][j] = 1;if(!flag1 && !flag2)ans = (ans + solve2(totx,toty) + p)%p;return ans;
}
void work()
{ ll ans = 0;rep(i,0,(1<<k)-1){if(num[i] % 2 == 1) ans = (ans - solve1(i) + p) % p;else ans = (ans + solve1(i)) % p;} printf("%lld\n",ans);   return;
}
void pre()
{rep(i,1,(1<<k)-1){int x = i;while(x){num[i] += x & 1;x /= 2;}}C[0][0] = 1;rep(i,1,n){C[i][0] = C[i][i] = 1;rep(j,1,i-1)C[i][j] = (C[i-1][j] + C[i-1][j-1]) % p;}jc[0] = 1;rep(i,1,n)jc[i] = 1ll * jc[i - 1] * i % p;}
void swap_(int x,int y){rep(i,1,n) swap(Map[i][x],Map[i][y]);return;}
void move()//行列交换
{int rank[40];rep(i,1,n/2) rank[i] = i * 2 - 1 , rank[n-i+1] = i * 2;if(n % 2 == 1) rank[n/2+1] = n;rep(i,1,n)while(rank[i] != i){swap(Map[i] , Map[rank[i]]);swap(rank[i],rank[rank[i]]);}rep(i,1,n/2) rank[i] = i * 2 - 1 , rank[n-i+1] = i * 2;if(n % 2 == 1) rank[n/2+1] = n;rep(i,1,n)while(rank[i] != i){swap_(i,rank[i]);swap(rank[i],rank[rank[i]]);}return;
}
void init()
{n = read();k = read();rep(i,1,n) Map[i][i] = 2 , Map[i][n-i+1] = 3; if(n % 2 == 1) Map[n/2+1][n/2+1] = 4;rep(i,1,k){int x = read() , y = read();if(Map[x][y])Map[x][y] = 10 + Map[x][y];else Map[x][y] = 1;}move();int tot = 0;rep(i,1,n)rep(j,1,n)if(Map[i][j] == 1 || Map[i][j] / 10 == 1)a[++tot].x = i,a[tot].y = j;return;
}
int main()
{ init();pre();work();return 0;
} 

2018.8.4T3(大容斥)相关推荐

  1. Four-tuples (2018山东省省赛 容斥定理)

    感谢大佬的博客: https://blog.csdn.net/qq_41021816/article/details/80328475 开始的时候不知道如何求满足性质pi的元素个数,知道参考了上面的大 ...

  2. ACM-ICPC 2018 沈阳赛区网络预赛 Spare Tire(容斥+公式推)

    A sequence of integer \lbrace a_n \rbrace{an​} can be expressed as: \displaystyle a_n = \left\{ \beg ...

  3. 2019牛客国庆集训派对day2 K 2018(容斥)

    链接:https://ac.nowcoder.com/acm/contest/1107/K 来源:2019牛客国庆集训派对day2 题目描述   Given a, b, c, d, find out ...

  4. UOJ#449. 【集训队作业2018】喂鸽子 min-max容斥,FFT

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ449.html 题解 设 f(i) 表示给 i 只鸽子喂食使得至少一只鸽子被喂饱的期望次数,先 min-max容斥 一下. ...

  5. [集训队作业2018]小Z的礼物(min-max容斥,插头dp)

    传送门 这种求 "取到所有物品的期望时间" 的题一般都用 min−maxmin-maxmin−max容斥 解决: 设t(i,j)t(i,j)t(i,j)为取到格子(i,j)(i,j ...

  6. 集训队作业2018: 青春猪头少年不会梦到兔女郎学姐(多限制容斥)(生成函数)(组合数学)

    题意 给定 nnn 种颜色的球,第 iii 种颜色的球数量为 aia_iai​ 个,一种排列的贡献可以如下计算:先把这个序列首尾相连,然后把所有相邻且颜色相同的段拿出来,贡献为他们的长度之积,求所有排 ...

  7. 【集训队作业2018】青春猪头少年不会梦到兔女郎学姐(容斥)(分治FFT)

    简要题意: 给定 nnn 种颜色的球,第 iii 种颜色的球数量为 aia_iai​ 个,一种排列的贡献可以如下计算:先把这个序列首尾相连,然后把所有相邻且颜色相同的段拿出来,贡献为他们的长度之积,求 ...

  8. Leetcode 552.学生出勤记录‖ 动态规划+容斥

    题目链接:传送门 可以用字符串表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤.迟到.到场).记录中只含下面三种字符: 'A':Absent,缺勤 'L':Late,迟到 'P':P ...

  9. 【GDOI2016模拟3.16】幂(容斥 + 模型复杂转化)

    [GDOI2016模拟3.16]幂 \(X\in[1,A],Y\in[1,B]\),问:\(x^y\)的不用取值个数. \(A,B\)都是\(10^9\)级别. 然后我们开搞. 首先,假设一个合法的\ ...

最新文章

  1. 崇拜的Unix大牛,记录之。
  2. opencv Mat类
  3. julia(5)-变量与内部常量
  4. 直播|百度AI开发者大会深度学习直播课程表
  5. CGCTF-Web-md5 collision
  6. Hbuilder MUI 下拉选择与时间选择器
  7. C# OpenTK教程 - 1.2 你好三角形
  8. 京东618期间将累计发放百亿消费券
  9. 9-13 ruby环境准备
  10. 实体类在set字段时报空指针异常
  11. PHP 输入一棵二叉树和一个数字n,要求找出路径和为n的所有路径
  12. Javascript特效:关闭小广告
  13. 星环Transwarp Data Hub大数据安装学习
  14. 企业资源计划(ERP)原理与实践第一章
  15. 【笔记整理】数字信号处理复习——FT、DTFT、DFT和FFT之间的关系
  16. c语言报告对老师的致谢,论文对老师的致谢
  17. vuetify,nginx与cors的使用
  18. 趣头条疯狂777就是坑货,根本不是纯随机
  19. 《一条狗的使命》观后感
  20. 群晖|半洗白后moments正常显示人像、主题、预览

热门文章

  1. 【算法岗求职笔记】降维 · 五问五答
  2. 为什么程序员越跳槽收入越高?
  3. 全志Tina Linux Camera 摄像头模块开发指南 全网最详细版本支持百问网T113-Pro DongshanPI-NezhaD1-H DongshanPI-D1s V853-Pro等开发板
  4. nodename nor servname provided的解决
  5. 同花顺_知识_看盘技法
  6. k22.第九章 K8s进阶篇-高级调度计划任务临时容器 (三)
  7. 跑步装备推荐:跑步运动装备清单分享
  8. C++结构体定义及申请空间
  9. 咕咕的的复复读读机机 ccpc河南省赛
  10. python短信验证码 容联云