zoukankan      html  css  js  c++  java
  • 【bzoj1227】 SDOI2009—虔诚的墓主人

    http://www.lydsy.com/JudgeOnline/problem.php?id=1227 (题目链接)

    题意

      一个n*m的公墓,一个点上要么是墓地,要么是常青树,给出一个数K,并规定每块墓地的虔诚度是以这个墓地为中心上下左右分别选择K棵常青树的方案数。问整个公墓所有墓地的虔诚度之和。

    Solution

      看到棋盘范围大小与点的个数的悬殊差距,首先就想到了离散化,然而离散化之后怎么统计答案呢?

      考虑对于所有点以x轴为第一关键字,y轴为第二关键字进行排序,那么对答案有贡献的墓地坐标一定在已经出现过的x坐标和y坐标中,因为一块有贡献的墓地不可能上下或左右没有常青树。所以我们这里只考虑每次计算一列中的墓地对答案的贡献,然而怎么搞呢。。。

      不会了,请出hzwer:

      先离散横纵坐标

      按照y进行排序,从下往上处理每一行

      l[a],r[a],u[a],d[a]表示一个点上下左右的点数,可以预处理,也可以边做边记录

      如果a,b在同一行,则ans+=c(l[a]+1(包括a),k)*c(r[b]+1,k)再分别乘上ab间的每一个点的c(u[i],k)*c(d[i],k)

      但是这样复杂度为n^2

      于是我们要用树状数组维护a到b所有点的c(u[i],k)*c(d[i],k)之和

      可以这样考虑

      比如某一列某一行有一个点

      在扫描这行之下的时候,这个点是算在u[i]里的,但是扫描这行之上时算在了d[i]中

      于是我们从左往右处理某行的某一个点时,要将树状数组中该点横坐标位置上的数进行修改

      修改的值为就是现在的c(u[i],k)*c[d[i],k]减去原来的,也就是c(u[i],k)*c[d[i],k]-c(u[i]+1,k)*c[d[i]-1,k]

      于是就是树状数组维护一下ok了。

    细节

      最后答案要加模再取模,因为可能减成负数。

    代码

    // bzoj1227
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define inf 2147483640
    #define MOD 2147483648ll
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=1000010;
    struct data {int x,y;}a[maxn];
    LL C[maxn][20],c[maxn];
    int n,m,W,K,q[maxn],h[maxn],l[maxn],num[maxn];
    
    bool cmp(data a,data b) {
    	return a.x==b.x ? a.y<b.y : a.x<b.x;
    }
    void calC() {
    	for (int i=0;i<=W*2;i++) C[i][0]=1;
    	for (int i=1;i<=W*2;i++)
    		for (int j=1;j<=min(i,K);j++)
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    }
    
    int lowbit(int x) {
    	return x&-x;
    }
    LL query(int x) {
    	LL s=0;
    	for (int i=x;i;i-=lowbit(i)) s=(s+c[i])%MOD;
    	return s;
    }
    void add(int x,LL val) {
    	for (int i=x;i<=W*2;i+=lowbit(i)) c[i]=(c[i]+val)%MOD;
    }
    
    int main() {
    	scanf("%d%d",&n,&m);
    	scanf("%d",&W);
    	for (int i=1;i<=W;i++) {
    		scanf("%d%d",&a[i].x,&a[i].y);
    		q[i*2-1]=a[i].x;q[i*2]=a[i].y;
    	}
    	scanf("%d",&K);
    	sort(q+1,q+1+W*2);
    	int tot=unique(q+1,q+1+W*2)-q-1;
    	for (int i=1;i<=W;i++) {
    		a[i].x=lower_bound(q+1,q+1+tot,a[i].x)-q;
    		a[i].y=lower_bound(q+1,q+1+tot,a[i].y)-q;
    		h[a[i].y]++;l[a[i].x]++;
    	}
    	sort(a+1,a+1+W,cmp);
    	calC();
    	LL cnt=0,ans=0;
    	for (int i=1;i<=W;i++) {
    		if (i>1 && a[i].x==a[i-1].x) {
    			cnt++;
    			LL t1=query(a[i].y-1)-query(a[i-1].y);
    			LL t2=C[cnt][K]*C[l[a[i].x]-cnt][K]%MOD;
    			ans=(ans+t1*t2%MOD)%MOD;
    		}
    		else cnt=0;
    		num[a[i].y]++;
    		LL tmp=C[num[a[i].y]][K]*C[h[a[i].y]-num[a[i].y]][K];
    		tmp-=C[num[a[i].y]-1][K]*C[h[a[i].y]-num[a[i].y]+1][K];
    		tmp=(tmp+MOD)%MOD;
    		add(a[i].y,tmp);
    	}
    	printf("%lld",(ans+MOD)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    [BZOJ4698][SDOI2008]Sandy的卡片(后缀自动机)
    [NOI2015]小园丁与老司机(DP+上下界最小流)
    [BZOJ2007][NOI2010]海拔(对偶图最短路)
    [NOI2018]屠龙勇士(exCRT)
    [NOI2018]归程(可持久化并查集,Kruskal重构树)
    [BZOJ2823][BZOJ1336][BZOJ1337]最小圆覆盖(随机增量法)
    [BZOJ1069][SCOI2007]最大土地面积(水平扫描法求凸包+旋转卡壳)
    [BZOJ1143][CTSC2008]祭祀river(Dilworth定理+二分图匹配)
    [BZOJ3160]万径人踪灭(FFT+Manacher)
    [NOI2015]寿司晚宴
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6218348.html
Copyright © 2011-2022 走看看