前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >P2154 [SDOI2009]虔诚的墓主人

P2154 [SDOI2009]虔诚的墓主人

作者头像
用户2965768
发布2019-10-22 19:59:38
2510
发布2019-10-22 19:59:38
举报
文章被收录于专栏:wym

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/qq_41603898/article/details/101852271

题意: 给出n,m ( n,m<=1e9 ), 表示一个矩阵 (0,0)到(n,m)有 w 棵(w<=1e5)常青树,求这个矩阵的虔诚度,虔诚度的定义如下: 对于矩阵上任意一点,正上下左右方向有恰好 k 棵(k<=10)常青树的方案数。 例如 某一点四个方向依次有 2,2,2,3此时 k = 2, 则虔诚度为 3。 保证 w 棵常青树位置各不相同,并给出w个坐标每个代表一颗常青树。答案对2,147,483,648 取模。

解: 先离散化坐标, 先按 y ,x 为一二键值从小到大排序, 给 y 离散化,并记录每 列有多少常青树, 再按照x,y为一二键值排序,给 x离散化,记录每行有多少常青树。显然,每个空地方案数 = C(up,k)*C(down,k)*C(left,k)*C(right,k)%MOD;

接下来,对每一行而言,相邻两常青树之间的空地 左右常青树的个数是相同的,这些空地上下常青树的个数不同,用树状数组记录每列的贡献,详见代码。

代码语言:javascript
复制
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll MOD=2147483648;
const int MAXN=100005;
ll num[MAXN];
ll num2[MAXN];
ll n,m,w,k;
struct N{
	ll x,y;
}point[MAXN];
bool cmp1(N p1,N p2){
	if(p1.x!=p2.x)return p1.x<p2.x;
	return p1.y<p2.y;
}         
bool cmp2(N p1,N p2){
	if(p1.y!=p2.y)return p1.y<p2.y;
	return p1.x<p2.x;
}    
ll h[MAXN],l[MAXN]; 
ll r[MAXN];
ll b[MAXN],tmp[MAXN];
void add(int x,ll v){
	while(x<MAXN){
		b[x]+=v;
		x+=(x&(-x));
	}
}
ll sum(int x){
	ll res = 0;
	while(x){
		res+=b[x];
		x-=(x&(-x));
	}
	return res;
}     
ll C[MAXN][20];
int main()
{
	ll cnt = 0;
	scanf("%lld %lld",&n,&m);
	scanf("%lld",&w);
	for(int i=1;i<=w;i++)scanf("%lld %lld",&point[i].x,&point[i].y);
	scanf("%lld",&k);
	sort(point+1,point+1+w,cmp2);
	
	for (int i = 0; i <= w; i++) C[i][0] = 1;
    for (ll i = 1; i <= w; i++) for (ll j = 1; j <= min(i, k); j++)
        C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
        
	for(int i=1;i<=w;i++){
		if(i==1 || point[i].y!=point[i-1].y)cnt++;
		tmp[i] = cnt;
	}
	for(int i=1;i<=w;i++){
		point[i].y = tmp[i];
		num2[tmp[i]]++;//y 列 常青树的个数 
	}
	sort(point+1,point+1+w,cmp1);
	cnt = 0;
	for(int i=1;i<=w;i++){
		if(i==1 || point[i].x!=point[i-1].x)cnt++;
		tmp[i] = cnt; 
	}
	for(int i=1;i<=w;i++){
		point[i].x = tmp[i];
		num[tmp[i]]++;//x 行 常青树 的个数
	}
	ll ans = 0,col;//col 列向上 的个数 
	cnt = 0;
	for(int i=1;i<=w;i++){
		if(i==1 || point[i].x!=point[i-1].x)cnt = 0;
		col = point[i].y;
		ll v = ++h[col];
		v = v>=k && num2[col] - v >=k ? C[v][k]*C[num2[col] - v][k]%MOD : 0; cnt++;
		add(col,v-r[col]); r[col] = v;
		if(i==w || cnt <k || num[point[i].x] - cnt <k || point[i].x!=point[i+1].x || point[i+1].y - point[i].y <=1)
			continue;
		ans+= ( C[cnt][k]*C[num[ point[i].x ] - cnt][k]%MOD )*(sum(point[i+1].y - 1) - sum(point[i].y) )%MOD;
	}
	printf("%lld\n",(ans+MOD)%MOD);
	return 0; 
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/10/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档