原题链接:3017. 按距离统计房屋对数目 II
题目描述:
给你三个正整数 n
、x
和 y
。
在城市中,存在编号从 1
到 n
的房屋,由 n
条街道相连。对所有 1 <= i < n
,都存在一条街道连接编号为 i
的房屋与编号为 i + 1
的房屋。另存在一条街道连接编号为 x
的房屋与编号为 y
的房屋。
对于每个 k
(1 <= k <= n
),你需要找出所有满足要求的 房屋对 [house1, house2]
,即从 house1
到 house2
需要经过的最少 街道数为 k
。
返回一个下标从 1 开始且长度为 n
的数组 result
,其中 result[k]
表示所有满足要求的房屋对的数量,即从一个房屋到另一个房屋需要经过的最少 街道数为 k
。
注意 ,x
与 y
可以 相等。
输入输出描述:
示例 1:
输入:n = 3, x = 1, y = 3
输出:[6,0,0]
解释:让我们检视每个房屋对
- 对于房屋对 (1, 2),可以直接从房屋 1 到房屋 2。
- 对于房屋对 (2, 1),可以直接从房屋 2 到房屋 1。
- 对于房屋对 (1, 3),可以直接从房屋 1 到房屋 3。
- 对于房屋对 (3, 1),可以直接从房屋 3 到房屋 1。
- 对于房屋对 (2, 3),可以直接从房屋 2 到房屋 3。
- 对于房屋对 (3, 2),可以直接从房屋 3 到房屋 2。
示例 2:
输入:n = 5, x = 2, y = 4
输出:[10,8,2,0,0]
解释:对于每个距离 k ,满足要求的房屋对如下:
- 对于 k == 1,满足要求的房屋对有 (1, 2), (2, 1), (2, 3), (3, 2), (2, 4), (4, 2), (3, 4), (4, 3), (4, 5), 以及 (5, 4)。
- 对于 k == 2,满足要求的房屋对有 (1, 3), (3, 1), (1, 4), (4, 1), (2, 5), (5, 2), (3, 5), 以及 (5, 3)。
- 对于 k == 3,满足要求的房屋对有 (1, 5),以及 (5, 1) 。
- 对于 k == 4 和 k == 5,不存在满足要求的房屋对。
示例 3:
输入:n = 4, x = 1, y = 1
输出:[6,4,2,0]
解释:对于每个距离 k ,满足要求的房屋对如下:
- 对于 k == 1,满足要求的房屋对有 (1, 2), (2, 1), (2, 3), (3, 2), (3, 4), 以及 (4, 3)。
- 对于 k == 2,满足要求的房屋对有 (1, 3), (3, 1), (2, 4), 以及 (4, 2)。
- 对于 k == 3,满足要求的房屋对有 (1, 4), 以及 (4, 1)。
- 对于 k == 4,不存在满足要求的房屋对。
提示:
2 <= n <= 10^5
1 <= x, y <= n
解题思路:
这个题目我感觉是比较难的,赛时写了一个小时的分类讨论没写出来,都给我讨论麻了,赛后学习了一下
灵神的思路,然后来总结一下大概思路,首先如果不考虑x和y之间的边,那么就很简单了,对于任意一个位置i,i左边的距离为[1,i-1],右边的点距离分别为[1,n-i],这个对于每个点计算一下就行了,但是这个题难就难在还在(x,y)之间建立了一条边,我们要考虑的就是这条边产生的影响,由于这个题目是具有对称性的,所以我们只需要考虑左半部分即可,右半边对称计算即可。
当1<=i<=x时:
对于[y,n]这个区间的点的距离肯定都变短了,由原来的[y-i,n-i]变为了[x-i+1,x-i+1+n-y]
- 对于[x+1,y-1]这个区间内的点一部分变短了,一部分没变,我们来推一下距离变短的那一部分点应该满足什么要求,原来的距离是j-i,现在就是x-i+1+y-j,距离变短意味着j-i>x-i+1+y-j,也就是说j>(x+1+y)/2,也就是j>=(x+1+y)/2+1,所以对于[x+1,y-1]这个区间内的点,对于j>=(x+1+y)/2+1时,距离变短了,我们把这部分原来计算的贡献撤销,加上这个新的贡献。
- 对于y<=i<=n时,是对称的,对称组为(i,n+1-i),(x,n+1-x),(y,n+1-y),然后采用同样的计算方式即可。
当i<(x+y)/2时:
对于[y,n]这个区间的点的距离肯定都变短了,由原来的[y-i,n-i]变为了[i-x+1,i-x+1+n-y]
- 对于[x+1,y-1]这个区间内的点一部分变短了,一部分没变,我们来推一下距离变短的那一部分点应该满足什么要求,原来的距离是j-i,现在就是i-x+1+y-j,距离变短意味着j-i>i-x+1+y-j,也就是说j>i+(y-x+1)/2,也就是j>=i+(y-x+1)/2+1,所以对于[x+1,y-1]这个区间内的点,对于j>=i+(y-x+1)/2+1时,距离变短了,我们把这部分原来计算的贡献撤销,加上这个新的贡献。
- 对于i>(x+y+1)/2时,是对称的,对称组为(i,n+1-i),(x,n+1-x),(y,n+1-y),然后采用同样的计算方式即可。
上面我们已经通过分类讨论和推公式推导出了计算公式,由于上面每次都是对一段连续的区间进行+1或者-1,对于这一操作我们可以差分数组进行维护。
时间复杂度:差分时间复杂度为O(1),枚举时间复杂度为O(n),最后对于差分数组求前缀和求出原始数组时间复杂度为O(n),所以最终时间复杂度为O(n)。空间复杂度:使用了一个差分数组,空间复杂度为O(n)。
cpp代码如下:
cpp
class Solution {
typedef long long LL;
public:
vector<long long> countOfPairs(int n, int x, int y) {
if(x>y)swap(x,y); //让x是较小的那个数,y是较大的那个数,方便处理
vector<LL>s(n+1); //差分数组
auto add=[&](int l,int r,int v){ //差分处理函数
if (l>r) {
return;
}
s[l]+=v,s[r+1]-=v;
};
auto update=[&](int i,int x,int y){ //对于1<=i<=x这一部分的处理
add(y-i,n-i,-1);
int dec=y-x-1;
add(y-i-dec,n-i-dec,1);
int j=(x+1+y)/2+1;
add(j-i,y-1-i,-1);
add(x-i+2,x-i+1+y-j,1);
};
auto update2=[&](int i,int x,int y){ //对于x<i<(x+y)/2这一部分的处理
add(y-i,n-i,-1);
int dec=(y-i)-(i-x+1);
add(y-i-dec,n-i-dec,1);
int j=i+(y-x+1)/2+1;
add(j-i,y-1-i,-1);
add(i-x+2,i-x+1+y-j,1);
};
for(int i=1;i<=n;i++)
{
add(1,i-1,1);
add(1,n-i,1); //先加上不考虑x和y之间边时的贡献
if(x+1>=y)continue; //x+1>=y不对初始贡献产生影响
//分类谈论分四种情况讨论,左半边通过上述函数直接计算,右半边对称之后通过上述函数计算
if(i<=x)update(i,x,y);
else if(i>=y)update(n+1-i,n+1-y,n+1-x);
else if(i<(x+y)/2)update2(i,x,y);
else if(i>(x+y+1)/2)update2(n+1-i,n+1-y,n+1-x);
}
LL sum=0;
vector<LL>ans(n);
//根据差分数组求出原始数组
for(int i=0;i<n;i++)
{
sum+=s[i+1];
ans[i]=sum;
}
return ans;
}
};