[SDOI2009] 虔诚的墓主人

[SDOI2009] 虔诚的墓主人

题目大意\(N\times 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\)

猜你喜欢

转载自www.cnblogs.com/LMSH7/p/9552477.html