6.22 每日五题(1/5)

先切一道紫题

洛谷P4648

[IOI2007] pairs 动物对数 - 洛谷

题意:在一维、二维、三维(三种情况)统计曼哈顿距离小于等于D的点对数很有意思的一道题

分类讨论一下

一维:

滑动窗口模版题

二维:

二维曼哈顿距离转切比雪夫距离️

链接

根据切比雪夫距离的性质,可以一维滑动窗口,一维区间求和(用数据结构维护一下)

这里有个小细节,因为区间的左端点可能为负数,需要加上一个大数再放进bit里

三维:

同样先转换一下距离(三维转成了四维),然后一维滑动窗口

剩下三维就是要统计边长为d的正方体内的点数

直接三维树状数组硬莽

记得离散化

再加上三维容斥

就完美结束了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll B,n,Dis,M;
ll a[100010];
struct Point{
	ll x,y;
}b[100010];
struct FourD{
	ll A,B,C,D;
}c[100010];
bool cmpX(const Point& a,const Point& b){
	return a.x < b.x;
}
bool cmpA(const FourD& a,const FourD& b){
	return a.A < b.A;
}
ll bit[200010];
ll LSB(ll i){
	return i & (-i);
}
const ll twoM = 75010;
ll psq(ll i){
	ll res = 0;
	i = min(i,twoM * 2 - 1LL);
	for( ; i > 0; i -= LSB(i)) res += bit[i];
	return res;
}
void add(ll i,ll len){
	for( ; i < twoM * 2; i += LSB(i)){
		bit[i] += len;
	}
}
ll Bit[310][310][310];
const ll thrM = 100;
void upd(ll x,ll y,ll z,ll len){
	for(ll i = x; i < thrM * 3; i += LSB(i)){
		for(ll j = y; j < thrM * 3; j += LSB(j)){
			for(ll k = z; k < thrM * 3; k += LSB(k)){
				Bit[i][j][k] += len;
			}
		}
	}
}
ll Psq(ll x,ll y,ll z){
	ll res = 0;
	x = min(x,thrM * 3 - 1LL);
	y = min(y,thrM * 3 - 1LL);
	z = min(z,thrM * 3 - 1LL);
	for(int i = x; i > 0; i -= LSB(i)){
		for(int j = y; j > 0; j -= LSB(j)){
			for(int k = z; k > 0; k -= LSB(k)){
				res += Bit[i][j][k];
			}
		}
	}
	return res;
}
ll query(ll x1,ll x2,ll y1,ll y2,ll z1,ll z2){
	//三维容斥
	ll res = Psq(x2,y2,z2);
	res -= Psq(x1,y2,z2) + Psq(x2,y1,z2) + Psq(x2,y2,z1);
	res += Psq(x1,y1,z2) + Psq(x1,y2,z1) + Psq(x2,y1,z1);
	res -= Psq(x1,y1,z1);
	return res;
}
int main(){	
	scanf("%lld %lld %lld %lld",&B,&n,&Dis,&M);
	if(B == 1){ //单调队列模板 
		for(ll i = 1; i <= n; i++){
			scanf("%lld",&a[i]);
		}
		sort(a + 1,a + n + 1); //排序
		ll ans = 0;
		ll l = 1;
		for(ll i = 1; i <= n; i++){
			while(l < i && a[i] - a[l] > Dis) l++; //没用的不要
			ans += i - l; //在范围里的都行 
		}
		printf("%lld",ans);
	}
	else if(B == 2){
		for(ll i = 1,x,y; i <= n; i++){
			scanf("%lld %lld",&x,&y);
			b[i].x = x + y,b[i].y = x - y; //距离的转换 
		}
		sort(b + 1,b + n + 1,cmpX); //排序 
		ll ans = 0;
		ll l = 1;
		for(ll i = 1; i <= n; i++){
			while(l < i && b[i].x - b[l].x > Dis) add(b[l].y + M,-1),l++; //没用的不要
			ans += psq(b[i].y + Dis + M) - psq(b[i].y - Dis - 1 + M); //在范围里的都行 
			add(b[i].y + M,1); //把自己加进去 
		}
		printf("%lld",ans);
	}
	else{
		for(ll i = 1,x,y,z; i <= n; i++){
			scanf("%lld %lld %lld",&x,&y,&z);
			c[i].A = x + y + z;
			c[i].B = x + y - z;
			c[i].C = x - y + z;
			c[i].D = -x + y + z;
		}
		sort(c + 1,c + n + 1,cmpA);
		ll ans = 0;
		ll l = 1;
		for(ll i = 1; i <= n; i++){
			while(l < i && c[i].A - c[l].A > Dis){
				upd(c[l].B + M,c[l].C + M,c[l].D + M,-1); //三维中没用的扔掉 
				l++;
			}
			ans += query(c[i].B - Dis - 1 + M,c[i].B + Dis + M,c[i].C - Dis - 1 + M,c[i].C + Dis + M,c[i].D - Dis - 1 + M,c[i].D + Dis + M);
			//硬莽三维
			upd(c[i].B + M,c[i].C + M,c[i].D + M,1); 
		}
		printf("%lld",ans);
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值