JOISC 2019 题解

Day1 T1

三维偏序板子。

Day1 T2

一种树分治。

随便找一个点当作根 dfs,随机一个点 \(x\),然后通过 query,能找出在链 \(rt \to x\) 上的点有哪些。以及每个点属于毛毛虫上哪个节点的子树。然后把这条链拆了,分治下去。由于度数限制,询问次数 37000 左右。(不懂怎么证的。

点击查看代码
#include <bits/stdc++.h>
#include <assert.h>
#include "meetings.h"
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <set>
#include <utility>
#include <vector>
using namespace std;
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
#define wj(name) freopen(#name".in", "r", stdin); \
                freopen(#name".out", "w", stdout);
inline int read() {
    int x = 0, v = 1, ch = getchar();

    while ('0' > ch || ch > '9') {
        if (ch == '-')
            v = 0;

        ch = getchar();
    }

    while ('0' <= ch && ch <= '9') {
        x = (x * 10) + (ch^'0');
        ch = getchar();
    }

    return v ? x : -x;
}

const int MAX = 2005;
vector<int> G[MAX], son[MAX];
int fa[MAX];

int rt, n;
int cmp(int x, int y) {
    return Query(rt, x, y) == x;
}
mt19937 rad(233);
void dfs(vector<int> S, int x) {
    vector<int> chain;
    vector<int>().swap(son[x]);
    //  vector<vector<int> > son(n);
    int y = rad() % S.size();
    y = S[y];

    for (int z : S)
        if (z != y) {
            int w = Query(x, y, z);

            if (w == z)
                chain.ep(z);
            else
                son[w].ep(z);
        }

    rt = x;
    sort(chain.begin(), chain.end(), cmp);
    chain.ep(y);

    for (int z : chain)
        Bridge(min(rt, z), max(rt, z)), rt = z;

    if (son[x].size())
        dfs(son[x], x);

    for (int z : chain)
        if (son[z].size())
            dfs(son[z], z);

}
void Solve(int N) {
    ::n = N;
    vector<int> S;

    for (int i = 1; i < N; ++ i)
        S.ep(i);

    dfs(S, 0);
}

Day 1 T3

咕咕咕

Day 2 T1

绝对值的去除是套路。正反做两遍。 考虑 \(h_j - h_i, i < j\) 的最大值。

扫描线。考虑一种套路,枚举右端点 \(j\),线段树上维护左端点答案。

对于每个 \(i\)\(i + a_i \le j \le i + b_i\)。那么开个 vector,当 \(j = i + a_i\),线段树上修改 \(i\) 位置为 \(h_i\),然后到 \(j = i + b_i + 1\),把 \(i\) 修改为 \(- h_i\)。然后区间加 \(h_j\),再区间减 \(h_j\)

维护历史最大值。

坑:

  • 先减后加。指 \(- h_i\) 先作用。
  • 单点修改时,不能写成 \(max_his = maxa = v\)。(估计就我这么瞎搞( )
点击查看代码
// 僕らタイムフライヤー \
   時を駆け上がるクライマー \
   時のかくれんぼ \
   はぐれっこはもういやなんだ
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
#define int long long 
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
#define wj(name) freopen(#name".in", "r", stdin); \
				freopen(#name".out", "w", stdout);
inline int read() {
	int x=0, v=1,ch=getchar();
	while('0'>ch||ch>'9') {
		if(ch=='-') v=0;
		ch=getchar();
	}while('0'<=ch&&ch<='9') {
		x=(x*10)+(ch^'0');
		ch=getchar();
	}return v?x:-x;
}
const int MAX = 2e5 + 5, INF = 2e9;
int n, T;
int h[MAX], a[MAX], b[MAX];
vector<int> G[MAX], U[MAX];
struct Q{ 
	int l, r;
	Q() {}
	void init() { l = read(), r = read();  }
} q[MAX];

// 查询区间历史最大值。 

struct node {
	// 区间加,区间历史最大值。
	int maxa, maxb;
	int add, hadd;
	void upd(int k1, int k2) {
		maxb = max(maxb, maxa + k2);
		maxa += k1;
		hadd = max(hadd, add + k2);
		add += k1;
	} 
} tr[MAX << 2];
#define rt 1, 1, n
#define ls x << 1, l, mid
#define rs x << 1 | 1, mid + 1, r
void ps(int x) {
	tr[x].maxa = max(tr[x<<1].maxa, tr[x<<1|1].maxa);
	tr[x].maxb = max(tr[x<<1].maxb, tr[x<<1|1].maxb);
}
void pd(int x) {
	tr[x<<1].upd(tr[x].add, tr[x].hadd);
	tr[x<<1|1].upd(tr[x].add, tr[x].hadd);
	tr[x].add = tr[x].hadd = 0;
}
void add(int x, int l, int r, int s, int t, int v) {
	if(s <= l && r <= t) { tr[x].upd(v,v); return ; }
	int mid = l + r >> 1; pd(x);
	if(s <= mid) add(ls, s, t, v);
	if(mid < t) add(rs, s, t, v);
	ps(x); 
}
void mdf(int x, int l, int r, int s, int v) {
	if(l == r) {
		tr[x].maxa = v;
		tr[x].maxb = max(tr[x].maxb, v);
		return ; 
	} pd(x);
	int mid = l + r >> 1;
	if(s <= mid) mdf(ls, s, v);
	else mdf(rs, s, v);
	ps(x);
}

int hans = -INF, ans[MAX];
void qry(int x, int l, int r, int s, int t) {
	if(s <= l && r <= t) {
		hans = max(hans, tr[x].maxb);
		return ;
	} int mid = l + r >> 1; pd(x);
	if(s <= mid) qry(ls, s, t);
	if(mid < t) qry(rs, s, t);
	ps(x);
}
void build(int x, int l, int r) {
	tr[x].add = tr[x].hadd = 0;
	if(l == r) {
		tr[x].maxa = tr[x].maxb = -INF;
		return ;
	} int mid = l + r >> 1;
	build(ls), build(rs), ps(x);
}
void solve() {
	build(rt);
	for(int j = 1; j <= n; ++ j) {
//		add(rt, j, j, h[j]);
		for(int i : U[j]) {
			int val = (j == i + a[i] ? - h[i] : - INF);
			mdf(rt, i, val);
		}
		int nl = max(1ll, j - b[j]), nr = j - a[j];
		if(nl <= nr) {
			add(rt, nl, nr,  h[j]);
			add(rt, nl, nr, - h[j]);
		}
		for(int id : G[j]) {
			hans = - INF; 
			qry(rt, q[id].l, j);
			ans[id] = max(ans[id], hans);
		}
	}
}
signed main() {
//	fin;
	n = read();
	for(int i = 1; i <= n; ++ i) {
		h[i] = read(), a[i] = read(), b[i] = read();
		if(i + a[i] <= n) U[i + a[i]].ep(i);
		if(i + b[i] + 1 <= n) U[i + b[i] + 1].ep(i);
	}
	T = read();
	for(int i = 1; i <= T; ++ i) {
		q[i].init();
		G[q[i].r].ep(i);
		ans[i] = -1;
	}
	solve();
	for(int i = 1; i <= n; ++ i) h[i] = INF - h[i];
	solve();
	for(int i = 1; i <= T; ++ i) printf("%lld\n", ans[i]); 
	return 0;
}
posted @ 2023-03-20 21:59  Lates  阅读(17)  评论(0编辑  收藏  举报