bzoj-1227 虔诚的墓主人

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

在一个nxm的矩形里给出一些w个点,这些是常青树的坐标;

求所有空地上下左右能组成的长度为k的十字架的总数;

1<=n,m<=10^9,1<=w<=100000,1<=k<=10


题解:

这道题思路很简单,就是代码不太好写;

首先如果是上下左右都没有树的点是必然没有虔诚度的,所以我们可以肆无忌惮的离散化一下;

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

(这里的l,r,u,d分别指某个点左右上下的常青树个数)

显然枚举每一块墓地是超时的,那么就考虑一次计算多个墓地;

对于在同一行两棵树之间的墓地来说,它们的 C ( l , k ) *C ( r , k )是相同的;

答案就是C ( l , k ) *C ( r , k ) * ∑C( u[i] , k )*C( d[i] , k );

求和的维护自然是树状数组,那么整道题的思路也就有了;

把所有树按行来排序,然后遍历维护出每棵树的l,r,u,d;

维护树状数组然后处理答案,时间复杂度是常数巨大的O(nlogn);


(代码写的非常非常烂。。。3900+ms我都不太好意思贴。。。


更新:                                15/07/21

今天考试碰上原题。。。

怒切之后发现有点黑科技可以用;

mod的数恰好是1<<31所以让int自然溢出就好;

最后输出答案就是&(1<<31-1)就是答案;

这样搞之后时间就是1864 ms啦;


代码:


15/07/21版:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define mod 2147483647
using namespace std;
struct node
{
	int x,y,tx,l,r,u,d;
	bool is;
}a[N];
int C[N][15];
int dis[N],tree[N],hash[N],last[N];
int cmp(node a,node b)
{
	if(a.y==b.y)
	return a.x<b.x;
	return a.y<b.y;
}
int lowbit(int x)
{
	return x&(-x);
}
void init()
{
	for(int i=0;i<N;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=min(i,10);j++)
			C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
}
void add(int x,int val)
{
	while(x<N)
	{
		tree[x]+=val;
		x+=lowbit(x);
	}
}
int query(int x)
{
	int ret=0;
	while(x)
	{
		ret+=tree[x];
		x-=lowbit(x);
	}
	return ret;
}
int main()
{
	freopen("cross.in","r",stdin);
	freopen("cross.out","w",stdout);
	int n,m,i,j,k,K,x,y,w,len,ans;
	init();
	scanf("%d%d%d",&n,&m,&w);
	for(i=1;i<=w;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		dis[i]=a[i].x;
	}
	scanf("%d",&K);
	sort(dis+1,dis+w+1);
	len=unique(dis+1,dis+w+1)-dis-1;
	sort(a+1,a+w+1,cmp);
	for(i=1,y=-1,k=0;i<=w;i++)
	{
		if(a[i].y!=y)
			y=a[i].y,k=0;
		a[i].l=k;
		k++;
		a[i].tx=lower_bound(dis+1,dis+len+1,a[i].x)-dis;
		a[i].d=hash[a[i].tx];
		hash[a[i].tx]++;
		if(a[last[a[i].tx]].y+1==a[i].y)
			a[last[a[i].tx]].is=1;
		last[a[i].tx]=i;
	}
	for(i=w,y=-1,k=0;i>=1;i--)
	{
		if(a[i].y!=y)
			y=a[i].y,k=0;
		a[i].r=k;
		k++;
		a[i].u=hash[a[i].tx]-a[i].d-1;
	}
	for(i=1,y=-1,ans=0;i<=w;i++)
	{
		if(a[i].is!=1)
		add(a[i].tx,C[a[i].u][K]*C[a[i].d+1][K]-(query(a[i].tx)-query(a[i].tx-1)));
		if(a[i].y==a[i+1].y)
			ans+=C[a[i].l+1][K]*C[a[i+1].r+1][K]*(query(a[i+1].tx-1)-query(a[i].tx));
	}
	printf("%d",(ans)&mod);
	return 0;
}


旧版:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define mod 2147483648ll
using namespace std;
typedef long long ll;
struct node
{
	ll dx,dy,x,y,l,r,u,d;
	bool is;
}a[N];
ll disx[N],disy[N];
ll tempx[N],tempy[N];
ll C[N][20],q[N];
ll tree[N];
int cmpll(ll a,ll b)
{
	return a<b;
}
int cmpnode(node a,node b)
{
	if(a.y==b.y)
	return a.x<b.x;
	return a.y<b.y;
}
ll mul(ll x,ll y)
{
	ll ret=0;
	while(y)
	{
		if(y&1)
		ret=(ret+x)%mod;
		x=(x+x)%mod;
		y>>=1;
	}
	return ret;
}
ll lowbit(ll k)
{
	return k&(-k);
}
void update(ll k,ll val)
{
	while(k<=100000)
	{
		tree[k]=(tree[k]+val)%mod;
		k+=lowbit(k);
	}
}
ll query(ll k)
{
	ll ret=0;
	while(k)
	{
		ret=(ret+tree[k])%mod;
		k-=lowbit(k);
	}
	return ret;
}
int main()
{
	ll n,m,w,i,j,k,x,y,cnt,lenx,leny,ans,st,en;
	scanf("%lld%lld%lld",&n,&m,&w);
	for(i=1;i<=w;i++)
	{
		scanf("%lld%lld",disx+i,disy+i);
		a[i].x=disx[i],a[i].y=disy[i];
	}
	scanf("%lld",&k);
	sort(disx+1,disx+1+w,cmpll);
	sort(disy+1,disy+1+w,cmpll);
	lenx=unique(disx+1,disx+1+w)-disx-1;
	leny=unique(disy+1,disy+1+w)-disy-1;
	sort(a+1,a+w+1,cmpnode);
	for(i=1,y=1,cnt=0;i<=w;i++)
	{
		a[i].dx=lower_bound(disx+1,disx+1+lenx,a[i].x)-disx;
		a[i].dy=lower_bound(disy+1,disy+1+leny,a[i].y)-disy;
		if(a[i].dy==y)
			a[i].l=cnt++;
		else
			tempy[y]=cnt,a[i].l=0,cnt=1,y=a[i].dy;
		a[i].d=tempx[a[i].dx];
		tempx[a[i].dx]++;
	}
	tempy[a[w].dy]=cnt;
	for(i=1;i<=w;i++)
	{
		a[i].u=tempx[a[i].dx]-a[i].d-1;
		a[i].r=tempy[a[i].dy]-a[i].l-1;
	}
	st=1,en=0;
	for(i=w,y=leny,x=w;i>0;i--)
	{
		if(a[i].dy!=y)
		{
			while(st<=en)	st++;
			for(j=x;j>i;j--)
				q[++en]=a[j].dx;
			x=i;
			y=a[i].dy;
		}
		while(q[st]>a[i].dx&&st<=en)	st++;
		if(q[st]==a[i].dx)	a[i].is=1;
	}
	for(i=0;i<=w;i++)
	{
		C[i][0]=1;
		for(j=1;j<=min(i,k);j++)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	y=a[1].dy,x=1;
	for(i=1,ans=0;i<=w;i++)
	{
		if(a[i].l+1>=k&&a[i].r>=k&&a[i+1].dx-1>a[i].dx)
		{
			ans=(ans+(((query(a[i+1].dx-1)-query(a[i].dx))%mod+mod)%mod)*
					   (C[a[i].l+1][k]*C[a[i].r][k]%mod))%mod;
		}
		if(a[i].is)
		update(a[i].dx,((C[a[i].u-1][k]*C[a[i].d+1][k]%mod)-
					   ((query(a[i].dx)-query(a[i].dx-1))%mod+mod)%mod+mod)%mod);
 		else
		update(a[i].dx,((C[a[i].u][k]*C[a[i].d+1][k]%mod)-
					   ((query(a[i].dx)-query(a[i].dx-1))%mod+mod)%mod+mod)%mod);
	}
	printf("%lld",ans%mod);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值