leetcode 3017. 按距离统计房屋对数目 II【推公式+分类讨论+对称性+差分】

原题链接:3017. 按距离统计房屋对数目 II

题目描述:

给你三个 正整数 n 、x 和 y 。

在城市中,存在编号从 1 到 n 的房屋,由 n 条街道相连。对所有 1 <= i < n ,都存在一条街道连接编号为 i 的房屋与编号为 i + 1 的房屋。另存在一条街道连接编号为 x 的房屋与编号为 y 的房屋。

对于每个 k1 <= k <= n),你需要找出所有满足要求的 房屋对 [house1, house2] ,即从 house1 到 house2 需要经过的 最少 街道数为 k 。

返回一个下标从 1 开始且长度为 n 的数组 result ,其中 result[k] 表示所有满足要求的房屋对的数量,即从一个房屋到另一个房屋需要经过的 最少 街道数为 k 。

注意x 与 y 可以 相等 

输入输出描述:

示例 1:

leetcode 3017. 按距离统计房屋对数目 II【推公式+分类讨论+对称性+差分】_第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:

leetcode 3017. 按距离统计房屋对数目 II【推公式+分类讨论+对称性+差分】_第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:

leetcode 3017. 按距离统计房屋对数目 II【推公式+分类讨论+对称性+差分】_第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代码如下:

class Solution {
    typedef long long LL;
public:
    vector countOfPairs(int n, int x, int y) {
        if(x>y)swap(x,y);   //让x是较小的那个数,y是较大的那个数,方便处理
        vectors(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=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;
        vectorans(n);
        //根据差分数组求出原始数组
        for(int i=0;i

你可能感兴趣的:(基础算法,数学,leetcode,算法,分类)