• [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)
  • 相关阅读:
    模态对话框可能导致程序崩溃
    C++实现将字符串循环左移n个位置
    Android Gallery图片显示和文字提示及Menu 菜单
    android代码实现ScaleAnimation
    Android系统内存管理的问题
    android打电话发短信
    android开发之屏幕设置
    输入法
    黄金周
    气筒
  • 原文地址:https://www.cnblogs.com/LMSH7/p/9552477.html
Copyright © 2020-2023  润新知