JZOJ 5750. 青青草原播种计划 (小性质+线段树)

Posted coldchair

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JZOJ 5750. 青青草原播种计划 (小性质+线段树)相关的知识,希望对你有一定的参考价值。

Description:

技术图片

(1 le n, Q le 5e5)

题解:

这题唯一的难度就是如何知道最小不可询问子草原绿值。

这个相当于问:有一堆数,每个可以用一次或不用,问最小凑不出来的自然数是多少。

首先把这些数排序,假设当前的答案是(p),初值(=1)

从小到大加入每个数(x),若(x le p),则(p+=x),否则答案就是(x)了。

这个贪心很妙。

我们可以把它改成:每次求(p)以内的数的和,看看和有没有(>p),若(>p),则继续,否则答案就是(p)

因为每次(p)都会至少(+=lp(上次的p)),所以复杂度相当于斐波拉契数列,近似为(log)

在线段树上实现这个就乘个(log)

其它的就是可持久化线段树,以及线段树合并一类简单问题。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;

const int N = 5e5 + 5;

int n, Q;
int op, x, y;
int rt[N];

#define pii pair<int, int>
multiset<pii> srt[N];

#define i0 t[i].l
#define i1 t[i].r
struct tree {
	int l, r, x;
	ll s;
} t[N * 35]; int tt;

int pl, pr, px;

void upd(int i) {
	t[i].x = min(t[i0].x, t[i1].x);
	t[i].s = t[i0].s + t[i1].s;
}

void add(int &i, int x, int y) {
	if(y < pl || x > pr) return;
	t[++ tt] = t[i], i = tt;
	if(x == y) {
		t[i].x += px;
		if(t[i].x < 0) t[i].x = 0;
		t[i].s = (ll) t[i].x * x;
		return;
	}
	int m = x + y >> 1;
	add(i0, x, m); add(i1, m + 1, y);
	upd(i);
}

int mer(int i, int j, int x, int y) {
	if(!i || !j) return i + j;
	int k = ++ tt;
	if(x == y) {
		t[k].x = t[i].x + t[j].x;
		t[k].s = t[i].s + t[j].s;
		return k;
	}
	int m = x + y >> 1;
	t[k].l = mer(t[i].l, t[j].l, x, m);
	t[k].r = mer(t[i].r, t[j].r, m + 1, y);
	upd(k); return k;
}

void ef(int i, int x, int y) {
	if(t[i].x > 0) return;
	if(x == y) { px = x; return;}
	int m = x + y >> 1;
	ef(i0, x, m);
	if(px > m) ef(i1, m + 1, y);
}

ll py;

void ft(int i, int x, int y) {
	if(y < pl || x > pr || !i) return;
	if(x >= pl && y <= pr) {
		py += t[i].s; return;
	}
	int m = x + y >> 1;
	ft(i0, x, m); ft(i1, m + 1, y);
}

int ans;

void qry(int i) {
	px = 5e5 + 1;
	ef(i, 1, 5e5);
	ans = px;
	pp("%d ", ans);
	
	ll lp = 0, p = 1;
	while(1) {
		pl = lp + 1, pr = p, py = 0;
		ft(i, 1, 5e5);
		if(py == 0) break;
		lp = p, p += py;	
	}
	pp("%lld
", p);
}

void dg(int i, int x, int y) {
	if(!t[i].s) return;
	if(x == y) {
		fo(j, 1, t[i].x) pp("%d ", x);
		return;
	}
	int m = x + y >> 1;
	dg(i0, x, m); dg(i1, m + 1, y);
}

int main() {
	freopen("forgive.in", "r", stdin);
	freopen("forgive.out", "w", stdout);
	scanf("%d %d", &n, &Q);
	fo(ii, 1, Q) {
		scanf("%d", &op);
		if(op == 1 || op == 2) {
			scanf("%d %d", &x, &y);
			x = (x + ans - 1) % n + 1;
			pl = pr = y; px = op == 1 ? 1 : -1;
			add(rt[x], 1, 5e5);
			srt[x].insert(pii(ii, rt[x]));
		} else
		if(op == 3) {
			scanf("%d %d", &x, &y);
			x = (x + ans - 1) % n + 1;
			y = (y + ans - 1) % n + 1;
			if(x == y) continue;
			rt[x] = mer(rt[x], rt[y], 1, 5e5);
			rt[y] = 0;
			srt[x].insert(pii(ii, rt[x]));
			srt[y].insert(pii(ii, rt[y]));
		} else
		if(op == 4) {
			scanf("%d", &x);
			x = (x + ans - 1) % n + 1;
			qry(rt[x]);
		} else {
			scanf("%d %d", &x, &y);
			x = (x + ans - 1) % n + 1;
			pii p;
			if(srt[x].empty() || (*srt[x].begin()).first >= y) {
				p = pii(0, 0);
			} else p = (*--srt[x].lower_bound(pii(y, 0)));
			qry(p.second);
		}
	}
	fo(i, 1, n) {
		dg(rt[i], 1, 5e5);
		pp("0
");
	}
}

以上是关于JZOJ 5750. 青青草原播种计划 (小性质+线段树)的主要内容,如果未能解决你的问题,请参考以下文章

青青草原--团队作业6:代码规范

青青草原--团队作业6:beta冲刺+事后诸葛亮博客汇总

青青草原--团队作业6:beta冲刺的第五天

[多校联考2019(Round 5)]青青草原的表彰大会(dp+组合数学)

青青草原--团队作业6:beta冲刺的第三天

青青草原--团队作业6:beta冲刺的第六天