cf918div4的F题

Problem - F - Codeforces

这道题有个很简单的思路,也有一个很难的思路,这个很难的思路用到了树状数组(但是是大佬写的),而简单的思路仅仅用到了归并排序求逆序对(也是一个大佬写的),而我连简单的思路都没想到,(*/ω\*)。

先说说简单的思路吧。

简单思路

cpp 复制代码
using i64 = long long;
i64 ans;
void mergesort(i64 left, i64 right, std::vector<i64>& a, std::vector<i64>& b) {
	if (left < right) {
		i64 mid = (left + right) / 2;
		mergesort(left, mid, a, b);
		mergesort(mid + 1, right, a, b);
		i64 i = left, j = mid + 1, ie = mid, je = right,k=0;
		while (i <= ie && j <= je) {
			if (a[i] <= a[j])b[k++] = a[i++];
			else {
				b[k++] = a[j++];
                //求逆序对的核心代码
				ans += ie - i + 1;
			}
		}
		while (i <= ie)b[k++] = a[i++];
		while (j <= je)b[k++] = a[j++];
		for (i64 h = left; h <= right; h++)a[h] = b[h - left];
	}
}
void solve() {
	int n;
	ans = 0;
	std::cin >> n;
	std::vector<i64>a(n), b(n),c(n),d(n),e(n);
	std::iota(d.begin(), d.end(), 0);
	for (int i = 0; i < n; i++)std::cin >> a[i] >> b[i];
	std::sort(d.begin(), d.end(),
		[&](int i, int j) {
			return a[i] < a[j];
		});
	for (int i = 0; i < n; i++)c[i] = b[d[i]];
	mergesort(0, n - 1, c,e);
	std::cout << ans << '\n';
}
int main() {
	int t;
	std::cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

只需要把起点从小到大排,终点也要跟着动,然后求终点的逆序对即可,我们要求的是一个区间全包含了多少个区间,在外面按照起点从小到大排序的时候,我们已经确保的下一个起点一定是包含于上一个起点的了,那么我们只要找到它对应的终点的左边的终点有多少个比它小的,及全包含的区间有多少个,说白了就是总的逆序对个数即可。

困难思路

这个代码是jiangly大佬写的,也许人家认为比逆序对的思路简单吧,(*/ω\*),我太菜了。

树状数组我也没完全理解,但是不妨碍我使用它

cpp 复制代码
using i64 = long long;

template <typename T>
struct Fenwick {
    int n;
    //a里面存的是一个区间,由大区间到小区间划分,起点都为0
    std::vector<T> a;
    //构造函数,不传参默认为0
    Fenwick(int n_ = 0) {
        init(n_);
    }
    //初始化n和数组a,assign(大小,类型{});
    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }
    //右端点和权值
    void add(int x, const T& v) {
        //更新父节点,
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }

    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }

    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }

    int select(const T& k) {
        int x = 0;
        T cur{};
        for (int i = 1 << std::log2(n); i; i /= 2) {
            if (x + i <= n && cur + a[x + i - 1] <= k) {
                x += i;
                cur = cur + a[x - 1];
            }
        }
        return x;
    }
};

void solve() {
    int n;
    std::cin >> n;
    //起点,终点
    std::vector<int> a(n), b(n), v;
    //预留两倍n空间
    v.reserve(2 * n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i] >> b[i];
        v.push_back(a[i]);
        v.push_back(b[i]);
    }
    std::sort(v.begin(), v.end());
    //相当于建了个系,因为每个起点或者终点都是不同的,把它放在坐标系(v)对应的位置
    //由于其唯一性,所以使用二分查找必定找到的是对应值,
    for (int i = 0; i < n; i++) {
        //找到a[i]和b[i]混合元素中第一个>=a[i]的值的下标
        a[i] = std::lower_bound(v.begin(), v.end(), a[i]) - v.begin();
        //找到a[i]和b[i]混合元素中第一个>=b[i]的值的下标
        b[i] = std::lower_bound(v.begin(), v.end(), b[i]) - v.begin();
    }
    //把起点和终点重新关联起来,且起点是有序的,有序起点排名对应无序终点排名
    std::vector<int> f(2 * n, -1);
    for (int i = 0; i < n; i++) {
        f[a[i]] = b[i];
    }
    //创建了一个树状数组
    Fenwick<int> fen(2 * n);
    i64 ans = 0;
    //总共只有2*n个点,i代表的是左端点,如果你的左端点比别人的左端点大,
    //那么只有别人和你greeting的份。
    for (int i = 2 * n - 1; i >= 0; i--) {
        //当右端点不为空时,说明是一个有效线段,由于i的递减性,所以下一个i肯定在上一个i左边
        if (f[i] != -1) {
            ans += fen.sum(f[i]);
            //传入右端点和权值
            fen.add(f[i], 1);
        }
    }
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }

    return 0;
}

题解都写在注释里面了,树状数组不会并不妨碍我们使用,我们排序完后f中存储是有序的ai对应的bi.由于是从起点大的一端枚举,所以会累加到小的一端且终点大于等于该值的线段上。最好先稍微了解一下树状数组的原理。就我目前的水平如果再碰到类似的题我应该会选择简单思路来写。

相关推荐
纳兰青华几秒前
拼图大师:从“暴力尝试“到“动态规划“的拆字艺术
java·算法·动态规划
星马梦缘10 分钟前
算法设计导论 课堂笔记
算法·动态规划·分治·时间复杂度·最优二叉树
luj_176811 小时前
星火科技助力边远地区防病攻坚
c语言·开发语言·c++·经验分享·算法
木木子2213 小时前
# 鸿蒙 ArkTS 实战:秒表 Stopwatch(示例 5)
数据结构·华为·list·harmonyos
geats人山人海13 小时前
数据结构第六章c语言 树的存储下二叉树的概念和存储
c语言·数据结构·算法
漂流瓶jz14 小时前
UVA-12627 奇怪的气球膨胀 题解答案代码 算法竞赛入门经典第二版
算法·图论·递归·aoapc·算法竞赛入门经典·uva·12627
jarvisuni14 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
kobesdu14 小时前
流形上的优化:SO(3)与SE(3)的广义加减法在FAST-LIO中如何简化状态估计
人工智能·算法·fastlio
白狐_79815 小时前
408 数据结构算法题 01:线性表暴力求解保分指南
java·数据结构·算法
乐观勇敢坚强的老彭15 小时前
C++信奥:开关门、开关灯问题
开发语言·c++·算法