【luogu CF1396D】Rainbow Rectangles(线段树)

Rainbow Rectangles

题目链接:luogu CF1396D

题目大意

给你一个 L*L 的矩形,里面有 n 个点,有各自的颜色。
然后问你有多少个端点是整点的矩形可以圈出所有颜色至少有一个点。

思路

考虑枚举区间的边界,枚举上边界。
然后再枚举下边界,然后考虑有多少答案。
(因为这个时候已近 n2 了)

然后你发现不能 O(1),看看 O(n) 行不行。
首先发现如果 [l,r] 可以,那对于 x>r[l,x] 都可以。
所以我们对于每个左边界,我们只要能求出最左的满足条件的右边界即可。

然后发现值域很大,所以要离散化,如果 xi,yi 代表离散化之后的位置值,设为 fi
那贡献就是 (yiyi1)(Lfi+1),然后上下端点也可以滑动,所以还有 (xr+1xr)(xlxl1)
不过上面那个我们可以拆一下:
i=1m(yiyi1)(Lfi+1)
i=1m(yiyi1)(L+1)i=1m(yiyi1)fi
ym(L+1)i=1m(yiyi1)fi
那问题就是怎么求后面这个,也就是如何求 fi

考虑考虑左边界右移。
那有的颜色会消失,你要找到右边第一个能出现的。
那我们可以设 nxtii 这一列的所有颜色下一次出现的列的最大值。
然后转移就是 fi+1=max(fi,nxti),那其实就是 nxti 的前缀最大值。

那我们 O(n) 转移了,也就到了 n3

考虑用下端点下手,能不能比如下端点往下,维护答案之类的。
那往下就是要加点,其实不好搞,我们考虑从下往上枚举下断点,那每次就是删点。
那我们可以用 set 记录每个颜色出现的列的坐标,那删除我们就找到上一个颜色是这一个的列,以及下一个。
如果上一个是 bef,下一个是 nxt,那就是 [bef+1,nxt] 里面的每个 xfx=max(fx,nxt)

那这个我们区间去最大我们可以用线段树来搞。
然后你上面那个 i=1m(yiyi1)fi 我们可以给区间赋一个权值为 yryl,每次赋值贡献要乘上这个东西。
但是最后你要的是区间求和,那我们似乎要用吉司机线段树?
其实不用,因为这个 fx 显然有着单调的性质,那我们直接二分出贡献的区间,然后直接变成区间赋值就好了。

时间复杂度是 nlog2n

代码

#include<set> #include<cstdio> #include<vector> #include<iostream> #include<algorithm> #define ll long long #define mo 1000000007 using namespace std; const ll N = 2005; ll n, k, L, x[N], y[N], xn, yn; vector <pair<ll, ll> > cx[N]; vector <ll> cy[N]; ll ans; struct node { ll x, y, c; }e[N]; multiset <ll> S, s[N]; ll nxt[N], R[N]; struct XD_tree { ll f[N << 2], lzy[N << 2], maxn[N << 2], sz[N << 2]; void up(ll now) { f[now] = (f[now << 1] + f[now << 1 | 1]) % mo; maxn[now] = max(maxn[now << 1], maxn[now << 1 | 1]); } void downc(ll now, ll x) { f[now] = sz[now] * x % mo; maxn[now] = x; lzy[now] = x; } void down(ll now, ll l, ll r) { ll mid = (l + r) >> 1; if (lzy[now] != -1) { downc(now << 1, lzy[now]); downc(now << 1 | 1, lzy[now]); lzy[now] = -1; } } ll find(ll now, ll l, ll r, ll k) {//第一个大于 k 的位置 if (l == r) return (maxn[now] > k) ? l : r + 1; down(now, l, r); ll mid = (l + r) >> 1; if (maxn[now << 1] > k) return find(now << 1, l, mid, k); else return find(now << 1 | 1, mid + 1, r, k); } void change(ll now, ll l, ll r, ll L, ll R, ll va) { if (L <= l && r <= R) { downc(now, va); return ; } down(now, l, r); ll mid = (l + r) >> 1; if (L <= mid) change(now << 1, l, mid, L, R, va); if (mid < R) change(now << 1 | 1, mid + 1, r, L, R, va); up(now); } void change_(ll L, ll R, ll va) { if (L > R) return ; ll pl = min(R, find(1, 1, yn, va) - 1); if (L <= pl) change(1, 1, yn, L, pl, va); } void build(ll now, ll l, ll r) { lzy[now] = -1; if (l == r) { sz[now] = y[l] - y[l - 1]; maxn[now] = R[l]; f[now] = maxn[now] * sz[now] % mo; return ; } ll mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); up(now); sz[now] = (sz[now << 1] + sz[now << 1 | 1]) % mo; } }T; void work(ll l) { for (ll i = 1; i <= yn; i++) cy[i].clear(); for (ll i = l; i <= xn; i++) for (ll j = 0; j < cx[i].size(); j++) cy[cx[i][j].first].push_back(cx[i][j].second); S.clear(); for (ll i = 1; i <= k; i++) { nxt[i] = yn + 1; S.insert(nxt[i]); s[i].clear(); s[i].insert(0); s[i].insert(nxt[i]); } for (ll i = yn; i >= 1; i--) { for (ll j = 0; j < cy[i].size(); j++) { S.erase(S.find(nxt[cy[i][j]])); nxt[cy[i][j]] = i; S.insert(nxt[cy[i][j]]); s[cy[i][j]].insert(i); } R[i] = y[*(--S.end())]; } T.build(1, 1, yn); for (ll r = xn; r >= l; r--) { ll sum = (1ll * y[yn] * (L + 1) % mo - T.f[1] + mo) % mo; (ans += sum * (x[r + 1] - x[r]) % mo * (x[l] - x[l - 1]) % mo) %= mo; for (ll i = 0; i < cx[r].size(); i++) { ll col = cx[r][i].second, Y = cx[r][i].first; s[col].erase(s[col].find(Y)); ll bef = *(--s[col].lower_bound(Y)); ll nxt = *s[col].lower_bound(Y); T.change_(bef + 1, Y, y[nxt]); } } } int main() { scanf("%lld %lld %lld", &n, &k, &L); for (ll i = 1; i <= n; i++) { ll z; scanf("%lld %lld %lld", &x[i], &y[i], &z); x[i]++; y[i]++; e[i] = (node){x[i], y[i], z}; } sort(x + 1, x + n + 1); xn = unique(x + 1, x + n + 1) - x - 1; x[xn + 1] = L + 1; sort(y + 1, y + n + 1); yn = unique(y + 1, y + n + 1) - y - 1; y[yn + 1] = L + 1; for (ll i = 1; i <= n; i++) { ll X = lower_bound(x + 1, x + xn + 1, e[i].x) - x; ll Y = lower_bound(y + 1, y + yn + 1, e[i].y) - y; cx[X].push_back(make_pair(Y, e[i].c)); } for (ll i = 1; i <= xn; i++) work(i); printf("%lld", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1396D.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示