CF1569D Inconvenient Pairs

题目大意

在一个1e6*1e6的网格中,有个n条垂直街道和m条水平街道,k个居民,其中居民只能在街道上。要求求出有多少对村民(a,b)的最短路径大于他们的|Xa-Xb|+|Ya-Yb|,称为不方便路径。其中(a,b)和(b,a)为同一种答案,在计算中不应该算作两种。

思路

两个村民的最短路径大于他们的横坐标差的绝对值+纵坐标差的绝对值。如果其中一个村民在一个垂直路径上,另一个村民在水平路径上,那么他们之间的最短路一定不是不方便路径,首先这两个路径一定有一个交点,一个村民从开始位置走到交点,再从交点走到另一个点就好了。如果有一个村民既在垂直路径上又在水平路径上,那么他和其他任何村民都不可能构造出不方便路径。也就是说只有当他们都只在水平路径或者都在只垂直路径上的时候才会出现两个村民之间的最短路径是不方便路径。下面分类讨论一下。

如果两个村民都在垂直路径上,如果他们所在的垂直路径不是同一条,并且他们俩之间没有一条水平路径,那么他们俩个就构成一个不方便路径。所以任意两个只在垂直路径上村民如果构成一个不方便路径,那么他们俩之间一定没有水平路径,所以他们俩下方的水平路径是同一条。那么的出一个结论,下方是同一条水平路径,且不在同一个垂直路径上的两个村民之间构成一个不方便路径。都只在水平路径上的话,不在同一个水平路径上,且右方是同一条垂直路径,那么这两个村民之间构成一个不方便路径。

有了以上思路,在循环存储村民数据的时候,把只在垂直路径上的村民记录下来,只在水平路径上的村民记录下来,然后对只在垂直路径上的村民i, 他下方的水平路径为py,之前有v[py]个村民下方的路径也是这条路径,还要求村民不在同一个垂直路径上,用mp[px] [py]记录在垂直路径px上下方水平路径是py的节点有多少个,ans+=v[py]-mp[px] [py] ,然后让v[py]++,mp[px] [py]++,再继续循环就好了。

代码示例

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll vx[300005];
ll vy[300005];
int x[1000005],y[1000005];//第i垂直条路径的横坐标为x[i]
//第i条水平路径纵坐标为y[i]
int n,m,k;//垂直路径数量n,水平路径数量m,村民数量k
struct node
{
	int x,y;
}que1[300005];//只在垂直街道上的人
int tot1;//只在垂直街道上人的数量

struct node2
{
	int x,y;
}que2[300005];//只在水平街道上的人
int tot2;//只在水平街道上人的数量

map<ll,ll>mp1[1000005],mp2[1000005];
//mp1[py][px] 村民下方的水平路径编号为py,且x坐标为px的村民的数量
//mp2[px][py] 村民右方的垂直路径编号为px,且y坐标为py的村民的数量
void init()
{
	tot1=0;
	tot2=0;
	memset(vx,0,sizeof(vx));
	memset(vy,0,sizeof(vy));
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&n,&m,&k);
		init();

		for(int i=1;i<=n;i++)
		{
			scanf("%d",&x[i]);
			mp2[i].clear();
		}

		for(int i=1;i<=m;i++)
		{
			scanf("%d",&y[i]);
			mp1[i].clear();
		}
		
		for(int i=1;i<=k;i++)
		{
			int tx,ty;
			scanf("%d%d",&tx,&ty);
			int px=upper_bound(x+1,x+1+n,tx)-x-1;//村民右方垂直路径编号,如果自己就在垂直路径上那么就是这条垂直路径编号
			int py=upper_bound(y+1,y+1+m,ty)-y-1;//村民下方水平路径编号,如果自己就在水平路径上那么就是这条水平路径编号
			if(x[px]==tx&&y[py]==ty)//在水平路径和垂直路径交点处,与其他所有村民都不构成不方便路径
			continue;
			else
			{
				if(x[px]==tx)//只在垂直路径上的村民
				{
					tot1++;
					que1[tot1].x=tx;
					que1[tot1].y=ty;
				}else//只在水平路径上的村民
				{
					tot2++;
					que2[tot2].x=tx;
					que2[tot2].y=ty;
				}
			}
		}

		ll ans=0;
		for(int i=1;i<=tot1;i++)//只在垂直路径上村民
		{
			int py=upper_bound(y+1,y+1+m,que1[i].y)-y-1;//第i个村民下方的水平路径编号为py
			ans+=vy[py]++ - mp1[py][que1[i].x]++;//vy[py]记录当前有多少个村民下方的水平路径编号是py,mp1[py][que1[i].x]记录下方水平路径编号是py且垂直路径坐标为que[i].x的村民的数量
		}
		for(int i=1;i<=tot2;i++)
		{
			int px=upper_bound(x+1,x+1+n,que2[i].x)-x-1;//第i个村民右方的垂直路径编号为px
			ans+=vx[px]++ - mp2[px][que2[i].y]++;//当前有vx[px]个村民右方的垂直路径编号为px,mp1[px][que2[i].y]记录右方水平路径编号是px且水平路径坐标为que2[i].y的村民的数量
		}
		cout<<ans<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值