【bzoj4150】[AMPPZ2014]The Staging 线段树

在舞台上有n个枪手,第i个枪手瞄准了第p[i]个枪手,将于第u[i]秒开枪。一个枪手如果成功开枪,
那么被瞄准的枪手会立刻死亡。
现在给出q次修改操作,请在一开始和每次修改操作后统计出最后存活的枪手个数。
p[i]互不相同,p[i] != i。
首先发现枪手形成若干环。于是可以用线段树维护,一个区间的开头那个人的状态对应结尾的状态,以及存活人数。
其他想法:u[i]折线图中只有下降的区间才有存活的人,不知可否用set。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
#define u t[x]
#define Lc t[x + x]
#define Rc t[x + x + 1]
using namespace std;
typedef long long LL;
const int N = 200005;
struct arr { int p[2], q[2], m; } t[N * 8]; // 0 -> alive 1 -> die
int n, m, c[N], l1[N], r1[N], p[N], lt, pre[N], a[N], pl, s1, tot, n1[N], n2[N], ql, qr, q, sum[N], T, ans;
bool vis[N], d[N];
void Upd(int x) {
	Rep(i, 0, 1) {
		u.p[i] = Rc.p[ Lc.p[i] ];
		u.q[i] = Lc.q[i] + Rc.q[ Lc.p[i] ];
	} u.m = (a[Lc.m] < a[Rc.m]) ? Lc.m : Rc.m;
}
void Build(int x, int l, int r) {
	if (l == r) {
		u.p[0] = 1, u.p[1] = d[l], u.q[0] = 1, u.m = l;
		return ;
	}
	int mid = l + r >> 1;
	Build(x + x, l, mid), Build(x + x + 1, mid + 1, r);
	Upd(x);
}
void Change(int x, int l, int r) {
	if (l == r) {
		u.p[1] = d[l], u.m = l; return ;
	}
	int mid = l + r >> 1;
	if (q <= mid) Change (x + x, l, mid);
	else Change (x + x + 1, mid + 1, r);
	Upd(x);
}
void Find(int x, int l, int r) {
	if (ql > r || qr < l) return ;
	if (ql <= l && r <= qr) {
		if (a[pl] > a[u.m]) pl = u.m; return ;
	}
	int mid = l + r >> 1;
	Find(x + x, l, mid), Find(x + x + 1, mid + 1, r);
}
void Qry(int x, int l, int r) {
	if (ql > r || qr < l) return ;
	if (ql <= l && r <= qr) {
		tot += u.q[s1]; s1 = u.p[s1]; return ;
	}
	int mid = l + r >> 1;
	Qry(x + x, l, mid), Qry(x + x + 1, mid + 1, r);
}
void Prework() {
	Rep(i, 1, n) if (!vis[i]) {
		int j = i; l1[++ m] = lt + 1;
		do {
			vis[j] = 1;
			a[++ lt] = c[j], d[lt + 1] = (c[ p[j] ] < c[j]); pre[ p[j] ] = j;
			n1[j] = lt, n2[j] = m; j = p[j];
		} while (j != i);
		r1[m] = lt, d[ l1[m] ] = d[lt + 1];
	}
	Build(1, 1, n);
	Rep(i, 1, m) {
		pl = l1[i], tot = 0;
		Rep(j, l1[i], r1[i]) if (a[pl] > a[j]) pl = j;
		s1 = 0, ql = pl, qr = r1[i], Qry(1, 1, n);
		ql = l1[i], qr = pl - 1, Qry(1, 1, n);
		tot -= s1, sum[i] = tot, ans += sum[i];
	}
	printf("%d\n", ans);
}
int main()
{
	scanf ("%d", &n);
	Rep(i, 1, n) scanf ("%d", &p[i]);
	Rep(i, 1, n) scanf ("%d", &c[i]);
	Prework();
	scanf ("%d", &T);
	while (T --) {
		int x, y, z;
		scanf ("%d%d", &x, &y);
		q = n1[x], a[q] = y, z = n2[x]; c[x] = y;
		d[q] = c[x] < c[ pre[x] ], d[ n1[ p[x] ] ] = c[ p[x] ] < c[x];
		Change(1, 1, n), q = n1[ p[x] ], Change(1, 1, n);
		ql = l1[z], qr = r1[z], pl = n1[x], Find(1, 1, n);
		tot = 0, s1 = 0;
		ql = pl, qr = r1[z], Qry(1, 1, n);
		ql = l1[z], qr = pl - 1, Qry(1, 1, n);
		tot -= s1, ans += tot - sum[z]; sum[z] = tot;
		printf("%d\n", ans);
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值