矩阵面积并 模板

扫描线思想
使用离散化的数字,在线段树上创建了长度不同的线段
维护最小值和最小值数量,最小值如果是0的话,就说明需要减去这部分
一定要注意标记下放和向上更新!

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stdio.h>
#include<stack>
#include<list>
#include<tuple>
#include<ctime>
#include<cstdlib>
#include<bit>
#include<sys/timeb.h>
using namespace std;
#define ffp(x,y,z) for(ll (x) = (y);(x)<=(z);(x++))
#define ffs(x,y,z) for(ll (x) = (y);(x)>=(z);(x--))
#define pii pair<ll ,ll> 
#define ll long long int
#define q_ (qd())
const double ex = 1e-7;
const int iINF = 0x3f3f3f3f;
const ll lINF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 998244353;
long long int qd() {
	long long w = 1, c, ret;
	while ((c = getchar()) > '9' || c < '0')
		w = (c == '-' ? -1 : 1); ret = c - '0';
	while ((c = getchar()) >= '0' && c <= '9')
		ret = ret * 10 + c - '0';
	return ret * w;
}
int stime()
{
	timeb ti;
	static bool f = 1;
	ftime(&ti);
	while (1)
	{
		if (f) { srand(ti.millitm * 117); f = 0; }

		int temp = rand();
		if (temp) { return temp > 0 ? temp : -temp; }
	}
}
ll gcd(ll a, ll b)
{
	if (a == 0)return b;
	return a % b == 0 ? b : gcd(b, a % b);
}
ll qs(ll a, ll b)
{
	a %= MOD;
	ll bei = a;
	a = 1;
	while (b)
	{
		if (b & 1) { a = a * bei % MOD; }
		bei = bei * bei % MOD;
		b >>= 1;
	}
	return a;
}
ll inv(ll a)
{
	return qs(a, MOD - 2);
}
static ll Max(ll a1 = -lINF, ll a2 = -lINF, ll a3 = -lINF, ll a4 = -lINF, ll a5 = -lINF)
{
	return max(max(max(max(a1, a2), a3), a4), a5);
}
static ll Min(ll a1 = lINF, ll a2 = lINF, ll a3 = lINF, ll a4 = lINF, ll a5 = lINF)
{
	return min(min(min(min(a1, a2), a3), a4), a5);
}

struct line
{
	ll minv;//最小值的值是?
	ll sum;//最小值的个数是?
	ll lon;//长度
	ll lz;
};//一段线段长

line operator+(line a, line b)
{
	if (a.minv == b.minv)
	{
		return { a.minv,a.sum + b.sum,a.lon + b.lon };
	}
	else if (a.minv < b.minv)
	{
		return { a.minv,a.sum,a.lon + b.lon };
	}
	else if (a.minv > b.minv)
	{
		return { b.minv,b.sum,a.lon + b.lon };
	}
}


int main()
{//矩形面积并,离散化数据
//需要特殊的线段树
//线段树要维护的值是:最小值,以及最小值的个数
// 有1e5个点,那就线段有1e5个
	ll n = q_;
	vector<tuple<ll, ll, ll, ll>>evt;
	vector<ll>vy;//离散化的y坐标
	vector<line>tr(200200 << 3);
	ffp(i, 1, n)
	{
		//上下扫的扫描线
		int x1 = q_;
		int y1 = q_;
		int x2 = q_;
		int y2 = q_;
		vy.push_back(y1);
		vy.push_back(y2);
		//以x坐标进行排序 x为行号
		evt.push_back({ x1,1,y1,y2 });
		evt.push_back({ x2,-1,y1,y2 });
	}

	sort(evt.begin(), evt.end());
	sort(vy.begin(), vy.end());
	vy.erase(unique(vy.begin(), vy.end()),vy.end());//离散化的去重

	auto pushdown = [&](int rt)->void
		{
			//	tr[rt].minv += tr[rt].lz;
			tr[rt * 2].lz += tr[rt].lz;
			tr[rt * 2].minv += tr[rt].lz;
			tr[rt * 2 + 1].lz += tr[rt].lz;
			tr[rt * 2 + 1].minv += tr[rt].lz;
			tr[rt].lz = 0;
		};
	auto updata = [&](int rt)->void
		{

		};
	auto bud = [&](auto&& bud, int rt, int l, int r)->void
		{
			if (l == r)
			{
				tr[rt].minv = 0;
				tr[rt].sum = vy[r] - vy[r - 1];
				tr[rt].lon = vy[r] - vy[r - 1];
				return;
			}//一段的长度是不同的
			bud(bud, rt * 2, l, (l + r) >> 1);
			bud(bud, rt * 2 + 1, ((l + r) >> 1) + 1, r);
			tr[rt] = tr[rt * 2] + tr[rt * 2 + 1];
		};
	auto push = [&](auto&& push, int rt, int l, int r, int y1, int y2,int va)->void
		{
			if (y1 <= l && r <= y2)
			{//更新当前的值
				tr[rt * 2].lz += va;
				tr[rt * 2 + 1].lz += va;
				tr[rt * 2].minv += va;
				tr[rt * 2 + 1].minv += va;
				tr[rt].minv += va;
				return;
			}
			if (tr[rt].lz)pushdown(rt);
			int med = (l + r) >> 1;
			if (y1 <= med)push(push, rt * 2, l, med, y1, y2, va);
			if (y2 > med)push(push, rt * 2 + 1, med + 1, r, y1, y2, va);
			tr[rt] = tr[rt * 2] + tr[rt * 2 + 1];
			//这里需要个向上更新
		//	updata(rt);
		};

	ll m = vy.size() - 1;
	bud(bud,1, 1, m);//只有m段

	ll prex = 0;//上一个的x所在的地方
	ll ans = 0;
	ll all = tr[1].sum;
	for (auto& e : evt)
	{
		auto [p, va, y1, y2] = e;
		ll minv = tr[1].minv;
		ll cnt = all;
		//每次先计算答案 然后再把一条线段放到线段树里,或者把一条线段拿走
		if (minv == 0)cnt = all -tr[1].sum;
		ans += (p - prex) * cnt;

		//线段树push
		int x1 = lower_bound(vy.begin(), vy.end(), y1) - vy.begin() + 1;
		int x2 = lower_bound(vy.begin(), vy.end(), y2) - vy.begin();
		push(push, 1, 1, m, x1, x2, va);
		prex = p;
	}

	cout << ans << endl;
	return 0;
}


/*
⡀⠎⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣄⠃⠈⣶⡛⠿⠭⣉⠛⠿⡿⠛⠉⣀⣠⣤⣭⡏⠴⢀⣴⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿
⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣷⣱⣬⠛⠉⠀⠀⢠⠀⠀⠀⢀⣀⠀⠉⠿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠈⡿
⠀⠀⠀⠀⠀⠀⠀⢀⢿⣿⣿⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀⡏⠀⠀⠀⠀⠈⠳⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠋⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠈
⠀⠀⠀⠀⠀⠀⠀⣸⠀⣿⣿⣿⣿⠟⠀⠀⠀⠂⠀⠀⢠⠀⠀⠀⠀⠀⠀⠀⠈⡀⠀⠀⠀⠻⣿⣿⣿⣿⣷⡀⠘
⠀⠀⠀⠀⠀⠀⠀⣧⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠙⣿⣿⣿⣿⣿⣄⣧
⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⣾⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣿⣆
⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⢂⠻⣿⣿⣿⣿⣿⣄
⠀⠀⠀⠀⠀⣿⣿⣿⣿⣹⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⡄⠈⢿⣿⣿⣿⣿⣆
⠀⠀⠀⠀⣿⣿⣿⣿⠁⡇⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠐⠸⠀⠀⠻⣿⣿⣿⣆⢦
⠀⠀⢠⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⣼⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡏⣧⠀⠀⠀⠀⠐⣇⠀⠀⠙⣿⣿⣿⡄⠙⣄
⠀⣴⣿⣿⣿⣿⠏⠀⢸⠀⠀⠀⠀⠀⠀⡿⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣃⣈⣦⠀⠀⠀⠀⢹⠀⠀⠀⠸⣿⣿⣿⠀⠀⠳⣀
⠋⣸⣿⣿⣿⡟⠀⠀⠀⡆⠀⠀⠀⠀⠀⡏⠙⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⢠⠀⠀⠀⢧⠀⠀⠀⠀⡇⠀⠀⠀⠘⣿⣿⣷⠀⠀⠘
⠀⣿⣿⣿⢩⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⣀⠀⢱⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠂⢀⣴⣶⣿⣿⡀⠀⠀⢻⠀⠀⠀⠀⠹⣿⣿⡄
⢸⣿⣿⠃⠈⠀⠀⢸⠀⣿⣆⠀⠀⠀⠀⣿⣿⣿⠷⠘⡀⠀⠀⠀⠀⠀⠀⢠⢹⡀⠈⡿⠻⣿⣛⢿⣿⣷⡀⠈⠀⠀⠀⠀⠀⢻⣿⣿
⣿⣿⣿⠀⠀⠀⠀⢸⠀⡇⣼⣄⠀⠀⠀⢻⣿⡄⠑⠑⣿⡀⠀⠀⠀⢀⠀⠂⠇⠀⠀⠖⠛⢿⣿⣿⣌⢿⣿⣿⡆⠀⠀⠀⠀⠀⣿⣿⡀
⣿⣿⡇⠀⠀⠀⠀⢸⠀⣾⣿⣿⡷⠿⣷⣤⣿⣿⡄⠀⠀⠀⠑⠤⡀⠀⠃⠀⠀⠀⠀⣿⣶⣿⣿⣿⣿⣆⠙⣿⣧⠀⠀⠀⠀⠀⣿⣿⡇
⣿⣿⠁⠀⠀⠀⠀⠘⣾⣿⣿⠁⣴⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠸⡏⠙⣿⠉⠻⣿⠀⠀⣿⠀⠀⠀⣄⠀⣿⢸⣷
⣿⣿⡇⠀⠀⠀⠀⠀⣿⣿⠁⠀⣿⣿⠋⣿⠏⠙⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⠀⢀⢻⠀⠀⢀⡟⢀⣿⣸⢃⠟
⣿⣿⣿⠀⡄⠀⠀⠀⠘⠻⡄⠀⢹⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⠀⢀⣿⠃⣿⣿⡗⠁
⣧⣿⣿⣧⢹⡀⠀⠀⠀⠱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⣴⣿⣿⣾⣿⣿⣿
⢿⠘⣿⣿⣿⣿⣤⠀⠢⡀⠱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣵⣿⣿⣿⣿⣿⣿⣿⣿⣷
⠀⠉⣿⣿⣿⡿⣿⠻⣷⣬⣓⣬⣄⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠉⠈⠈⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⠃⠼⢉⣿⣿⣿⣿⣿⣿⣿
⠀⠀⣿⣿⣿⣷⠀⠀⠀⠘⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⡏⠀⠀⢸⠀⢻⢿⣿⣿⡏⣿
⠀⢸⣿⣿⣿⣿⠀⠀⠀⠀⢻⣿⣿⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⠀⠀⠀⢸⠀⠀⢸⣿⣿⠘⡀
⢦⡿⣿⣿⣿⢿⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣶⣶⣦⡄⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠘⡄⠀⠈⣿⣿⡄⠱
⣴⠛⣾⣿⣿⢸⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⡄⠀⠀⠀⠀⠀⠀⠀⣯⠛⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⣇⠀⠀⣿⣿⣿
⠿⠀⣿⣿⣿⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⠟⠰⡾⠃⠀⠀⠀⠀⠀⠀⠀⠙⡟⠀⢻⣿⣿⣿⣿⣿⡆⠀⠀⠀⠸⠀⠀⠸⣿⣿⣷
⠆⢳⣿⣿⡇⠀⠀⠀⠀⠀⠀⣿⣿⣿⠛⠿⠿⢿⡟⠀⠀⠉⠦⣀⡤⢶⠀⠖⠲⠶⠊⠀⠀⠀⢻⡛⠛⠛⣿⣿⠀⠀⠀⠀⠃⠀⠀⢿⣿⣿
*/


posted @ 2025-08-09 15:25  粉紫系超人气月兔铃仙  阅读(6)  评论(0)    收藏  举报