[SDOI2009] 虔诚的墓主人
题目大意:(N imes M)的点阵,有的点是树木,定义一个空点的度数为正上,正左,正右,正下分别有(k)个点的选法
求点阵的总度数.
挺好的一道题,排列组合和数据结构糅合在一块
Solution
- 二项式定理预处理(C[W][K])。
- 读入,离散化(x)轴,(y)轴,记录每个(x)和每个(y)出现的次数,分别用(xcnt)和(ycnt)表示
- 然后用(x)从小到大排序,相同的用(y)从小到大排序,在(x)轴从左往右扫,每次(x)坐标变化时,记录一个(tt)表示这个树下方有多少颗树,记录一个(h)数组表示过程中某一行已经经过的树的个数。更新行,查询列,记录和。
- 树状数组中存的是某一行到现在(x)时左右两边组合数的结果,当不符合时,减去之前的。
- 要求取模2147483648,我们只需要自然溢出然后(& 2147483647)就好
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#define lowbit(x) x & -x//宏定义树状数组操作
using std::sort;
using std::min;
const int N = 1e6 + 10;
struct Node{
int x, y;
}a[N];
int n, m, w, k, col;
int c[N][16];
int tmp[N], xcnt[N], ycnt[N], h[N], r[N], bit[N];
inline void ins(int a, int b){
for(; a <= col; a += lowbit(a))
bit[a] += b;
}
inline int ask(int a){
int ans = 0;
for(; a; a -= lowbit(a)){
ans += bit[a];
}
return ans;
}
inline bool cmp1(Node a, Node b){
return a.x < b.x;
}
inline bool cmp2(Node a, Node b){
return a.y < b.y;
}
inline bool cmp3(Node a, Node b){
if(a.x == b.x) return a.y < b.y;
else return a.x < b.x;
}
inline void pre(){
c[0][0] = 1;
for(int i = 1; i <= w; ++i){
c[i][0] = 1;
for(int j = 1; j <= std::min(i, k); ++j){
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}
}
return;
}//二项式定理预处理组合数
inline void disp(){
int tt = 0;
sort(a + 1, a + w + 1, cmp1);
for(int i = 1; i <= w; ++i){
if(i == 1 || a[i].x != a[i - 1].x){
tt++;
}
tmp[i] = tt;
}
for(int i = 1; i <= w; ++i){
xcnt[a[i].x = tmp[i]]++;
}
sort(a + 1, a + w + 1, cmp2);
tt = 0;
for(int i = 1; i <= w; ++i){
if(i == 1 || a[i].y != a[i - 1].y){
tt++;
}
tmp[i] = tt;
}
for(int i = 1; i <= w; ++i){
ycnt[a[i].y = tmp[i]]++;
}
col = tt;//记录树状数组的“n”
return;
}//离散化
int main(){
scanf("%d %d %d", &n, &m, &w);
for(int i = 1; i <= w; ++i){
scanf("%d %d", &a[i].x, &a[i].y);
}
scanf("%d", &k);
pre();
disp();
sort(a + 1, a + w + 1, cmp3);
int tt = 0, ans = 0;
for(int i = 1, lh, lv; i <= w; ++i){//核心代码
if(i == 1 || a[i].x != a[i - 1].x) tt = 0;
lh = a[i].y;//lh表示当前树木的行坐标
if(++h[lh] >= k && ycnt[lh] - h[lh] >= k){//左右两边都符合
lv = c[h[lh]][k] * c[ycnt[lh] - h[lh]][k];//我们存到树状数组的东西
}else{
lv = 0;
}
ins(lh, lv - r[lh]);//如果不符合,就减去之前符合的,变成0
r[lh] = lv;
tt ++;//别忘了
if(i == w || a[i].x != a[i + 1].x || a[i + 1].y - a[i].y <= 1 || tt <k || xcnt[a[i].x] - tt < k)//上下都符合
continue;
ans += c[tt][k] * c[xcnt[a[i].x] - tt][k] * (ask(a[i + 1].y - 1) - ask(a[i].y));
}//这里上面空地正好是a[i + 1].y - 1,下面是a[i].y + 1,由于树状数组的操作,所以这样写
printf("%d", ans & 2147483647);//自然溢出
return 0;
}
Error
- 每次变动列时(tt)要清零,每次经过一棵常青树,(tt+1)