1. 程式人生 > >[BZOJ1178][Apio2009]CONVENTION會議中心(倍增 + 貪心 + 線段樹)

[BZOJ1178][Apio2009]CONVENTION會議中心(倍增 + 貪心 + 線段樹)

Address

洛谷 P3626
BZOJ 1178

Code

  • 先離散化區間端點
  • 預處理出對於每個區間 [ l i , r i
    ] [l_i,r_i]
    ,滿足 r i < l
    j r_i<l_j
    r j r_j 最小的區間 [
    l j , r j ] [l_j,r_j]
  • 下文稱之為「 i i 的後繼為 j j
  • 考慮一個小問題
  • 查詢在包含於 [ L , R ] [L,R] 的所有區間中,最多能選出多少個互不相交的區間
  • 先找到包含於 [ L , R ] [L,R] 且右端點最小的區間 [ l i , r i ] [l_i,r_i] (不存在則答案為 0 0
  • 問題轉化為 i i 至少跳多少次後繼之後右端點超過 R R 或者不存在後繼
  • 倍增 O ( n log n ) O(n\log n) 預處理後 O ( log n ) O(\log n) 查詢
  • 考慮貪心地編號從小往大,考慮每個區間是否可以成為答案
  • 設當前考慮區間 [ l , r ] [l,r]
  • 如果 [ l , r ] [l,r] 記憶體在一點,已經被之前選出的區間覆蓋,則這個區間不能成為答案
  • 否則設 l 0 l_0 為滿足 [ l 0 , l ] [l_0,l] 都沒有被覆蓋的最小的 l 0 l_0
  • r 0 r_0 為滿足 [ r , r 0 ] [r,r_0] 都沒有被覆蓋的最大的 r 0 r_0
  • q u e r y ( l , r ) query(l,r) 表示包含於 [ l , r ] [l,r] 的所有區間中最多選出多少個區間互不相交
  • 如果 q u e r y ( l 0 , l 1 ) + 1 + q u e r y ( r + 1 , r 0 ) q u e r y ( l 0 , r 0 ) query(l_0,l-1)+1+query(r+1,r_0)\ne query(l_0,r_0) 則區間 [ l , r ] [l,r] 不能成為答案
  • 否則區間 [ l , r ] [l,r] 可以成為答案,直接加入答案
  • 複雜度 O ( n log n ) O(n\log n)

Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define p2 p << 1
#define p3 p << 1 | 1

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

const int N = 2e5 + 5, M = N << 1, L = M << 2, LogN = 20;

int n, m, tm, tmp[M], fa[N][LogN], dep[N], ri[M], sum[L], add[L],
ansm, ans[N], pre0[L], suf0[L];

std::vector<int> itv[M];

struct node
{
	int l, r;
} a[N];

void build(int l, int r, int p)
{
	sum[p] = add[p] = 0;
	pre0[p] = suf0[p] = r - l + 1;
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, p2); build(mid + 1, r, p3);
}

void down(int p)
{
	add[p2] += add[p]; add[p3] += add[p];
	add[p] = 0;
}

void upt(int l, int r, int p)
{
	int mid = l + r >> 1, lp0, rp0, ls0, rs0;
	sum[p] = sum[p2] + sum[p3]
		+ (mid - l + 1) * add[p2] + (r - mid) * add[p3];
	lp0 = add[p2] ? 0 : pre0[p2];
	rp0 = add[p3] ? 0 : pre0[p3];
	ls0 = add[p2] ? 0 : suf0[p2];
	rs0 = add[p3] ? 0 : suf0[p3];
	pre0[p] = lp0 == mid - l + 1 ? lp0 + rp0 : lp0;
	suf0[p] = rs0 == r - mid ? rs0 + ls0 : rs0;
}

void change(int l, int r, int s, int e, int v, int p)
{
	if (l == s && r == e) return (void) (add[p] += v);
	int mid = l + r >> 1;
	down(p);
	if (e <= mid) change(l, mid, s, e, v, p2);
	else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
	else change(l, mid, s, mid, v, p2),
		change(mid + 1, r, mid + 1, e, v, p3);
	upt(l, r, p);
}

int ask(int l, int r, int s, int e, int p)
{
	if (l == s && r == e) return sum[p] + (r - l + 1) * add[p];
	int mid = l + r >> 1, res;
	down(p);
	if (e <= mid) res = ask(l, mid, s, e, p2);
	else if (s >= mid + 1) res = ask(mid + 1, r, s, e, p3);
	else res = ask(l, mid, s, mid, p2)
		+ ask(mid + 1, r, mid + 1, e, p3);
	return upt(l, r, p), res;
}

int askpre0(int l, int r, int s, int e, int