虔诚的墓主人 洛谷p2154

题目描述


小W是一片新造公墓的管理人。公墓可以看成一块N×M的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。


当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地。为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度。


一块墓地的虔诚度是指以这块墓地为中心的十字架的数目。一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k棵常青树。


小W希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少。


输入输出格式


输入格式:
输入文件religious.in的第一行包含两个用空格分隔的正整数N和M,表示公墓的宽和长,因此这个矩形公墓共有(N+1) ×(M+1)个格点,左下角的坐标为(0, 0),右上角的坐标为(N, M)。


第二行包含一个正整数W,表示公墓中常青树的个数。


第三行起共W行,每行包含两个用空格分隔的非负整数xi和yi,表示一棵常青树的坐标。输入保证没有两棵常青树拥有相同的坐标。


最后一行包含一个正整数k,意义如题目所示。


输出格式:
输出文件religious.out仅包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对2,147,483,648取模。


输入输出样例


输入样例#1: 复制
5 6
13
0 2
0 3
1 2
1 3
2 0
2 1
2 4
2 5
2 6
3 2
3 3
4 3
5 2
2
输出样例#1: 复制
6
说明


图中,以墓地(2, 2)和(2, 3)为中心的十字架各有3个,即它们的虔诚度均为3。其他墓


地的虔诚度为0。






对于30%的数据,满足1 ≤ N, M ≤ 1,000。


对于60%的数据,满足1 ≤ N, M ≤ 1,000,000。


对于100%的数据,满足1 ≤ N, M ≤ 1,000,000,000,0 ≤ xi ≤ N,0 ≤ yi ≤ M,1 ≤ W ≤ 100,000,1 ≤ k ≤ 10。


存在50%的数据,满足1 ≤ k ≤ 2。


存在25%的数据,满足1 ≤ W ≤ 10000。

对于一块墓地,它的答案就是C ( l , k ) *C ( r , k ) *C ( u , k ) *C ( d , k );

由于地图范围很大,所以不能一格一格枚举,观察到树的数量比较少,可以针对树进行枚举。

先对树的坐标进行离散化,然后一行一行的枚举,在枚举每一行的时候,左右的树的数量可以随时统计,左侧的为l,右侧的为numy-l,numy为在那一行树的总数,可以预处理。

那对于上下的树,用树状数组维护一个∑C( u[i] , k )*C( d[i] , k )即可;

由于最后的取模是2147483648,所以可以让int去溢出,最后答案为ans&2147483647.

#include<iostream>
#include<algorithm>
#define f(i,l,r) for(i=(l);i<=(r);i++)
#define MOD 2147483647
using namespace std;
const int MAXN=100005;
int n,m,w,k;
struct Node{
	int x,y;
}a[MAXN];
int tmp[MAXN],numx[MAXN],numy[MAXN];
int rang;
int C[MAXN][20],tree[MAXN];
int now[MAXN];
bool cmp1(Node a,Node b)
{
	return a.x<b.x;
}
bool cmp2(Node a,Node b)
{
	if(a.y==b.y) return a.x<b.x;
	return a.y<b.y;
}
inline void lisan()
{
	int i,j;
	sort(a+1,a+1+w,cmp1);
	j=0;
	f(i,1,w){
		if(i==1||a[i].x!=a[i-1].x) j++;
		tmp[i]=j;
	}
	rang=j;
	f(i,1,w)  numx[a[i].x=tmp[i]]++;
	sort(a+1,a+1+w,cmp2);
	j=0;
	f(i,1,w){
		if(i==1||a[i].y!=a[i-1].y) j++;
		tmp[i]=j;
	}
	f(i,1,w)  numy[a[i].y=tmp[i]]++;
}
inline void MakeTable()
{
	int i,j;
	C[0][0]=1;
	f(i,1,w){
		C[i][0]=1;
		f(j,1,min(i,k)){
			C[i][j]=C[i-1][j-1]+C[i-1][j];	
		}
	}
}
inline void add(int x,int d)
{
	int i;
	for(i=x;i<=rang;i+=(i&(-i))){
		tree[i]+=d;
	}
}
inline int query(int x)
{
	int i,res=0;
	for(i=x;i;i-=(i&(-i))){
		res+=tree[i];
	}
	return res;
}
int main()
{
	ios::sync_with_stdio(false);
	int i,j,l,ans=0;
	cin>>n>>m>>w;
	f(i,1,w){
		cin>>a[i].x>>a[i].y;
	}
	cin>>k;
	lisan();
	f(i,1,rang){
//		cout<<numy[i]<<"GG"<<endl;
	}
	MakeTable();
	f(i,1,w){
		int x=a[i].x,y=a[i].y;
		if(i>1&&y==a[i-1].y){
			l++;
			ans+=(C[l][k]*C[numy[y]-l][k])*(query(x-1)-query(a[i-1].x));
	//		cout<<i<<' '<<C[l][k]*C[numy[y]-l][k]<<' '<<query(x-1)-query(a[i-1].x)<<endl;
		}
		else l=0;
		now[x]++;
		int d=C[now[x]][k]*C[numx[x]-now[x]][k]-C[now[x]-1][k]*C[numx[x]-now[x]+1][k];
		add(x,d);
	}
	cout<<(ans&MOD)<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值