[SDOI2009] 虔诚的墓主人

[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)
原文地址:https://www.cnblogs.com/LMSH7/p/9552477.html