Codeforces 453E Little Pony and Lord Tirek (Splay + 主席树)

题意:

有n个马, 每个马刚开始有s[i]的能量,能量上限是t[i],增长速度是v[i], 有q个操作,每次操作把询问在时刻t,l到r的马的能量和是多少,并把这个区间所有马的能量清空。

思路:

对每只马来说,影响他的能量的是两次操作的时间间隔,因此可以用splay来维护n个马,并且把相邻并且操作时间相同的马放在同一个splay里面,然后询问就是把l到r里面的马放在同一个splay里面,并且可以在合并的时候计算出答案。由于合并的时候是一段区间经过相同的时间间隔的答案,可以用主席树预处理一下来统计答案。主席树按照时间来建,对每个马,在把它的能量加到满的时间点更新,也就是(ceil(m[i]/r[i])。由于刚开始可能会有能量,用两个主席树来搞,一个是统计上一次操作时间为0的,另一个统计上一次操作时间非0的。
这样做复杂度貌似是nlogn的,题解说是nlog^2n,并不知道是哪里有问题,不过splay常数大到爆炸。
这样做复杂度是nlogn,用线段树和主席树搞就是nlog^2n的,就是查询区间上次操作时间不相同的时候暴力往下查询。
而splay 跑了500+ms,线段树跑了900+ms,别人的zkw线段树跑了600+ms
#include <bits/stdc++.h>
using namespace std;

#define N 200020
#define LL long long
#define md (ll + rr >> 1)
#define pll pair<LL, LL>
#define MP make_pair
#define inf 0x3f3f3f3f


int n;
int v[N];

struct seg_t {
	int mx[N];
	int san[N], cnt;
	int rt[N], t[N];

	LL sum[N*20], sumV[N*20], tot;

	int ch[N*20][2];
	void init() {
		cnt = 0;
		for(int i = 1; i <= n; ++i) {
			if(v[i] == 0) t[i] = inf;
			else 
				t[i] = ceil(mx[i]  * 1.0 / v[i]);
			san[++cnt] = t[i];
		}
	}
	void add(int x) {
		san[++cnt] = x;
	}
	int haxi(int x) {
		return lower_bound(san + 1, san + cnt + 1, x) - san;
	}
	int update(int i, int x, int V, int M, int ll, int rr) {
		int k = ++tot;
		ch[k][0] = ch[i][0];
		ch[k][1] = ch[i][1];
		sum[k] = sum[i] + M;
		sumV[k] = sumV[i] + V;
		if(ll == rr) return k;
		if(x <= md) ch[k][0] = update(ch[i][0], x, V, M, ll, md);
		else ch[k][1] = update(ch[i][1], x, V, M, md + 1, rr);
		return k;
	}
	void lisan() {
		sort(san + 1, san + cnt + 1);
		cnt = unique(san + 1, san + cnt + 1) - san - 1;

		for(int i = 1; i <= n; ++i) {
			t[i] = haxi(t[i]);
			rt[i] = update(rt[i-1], t[i], v[i], mx[i], 1, cnt);
		}

	}

	LL query_sum(int i, int o, int x, int ll, int rr) {
		if(ll == rr) return sum[i] - sum[o];
		if(x <= md) return query_sum(ch[i][0], ch[o][0], x, ll, md);
		return sum[ch[i][0]] - sum[ch[o][0]] + query_sum(ch[i][1], ch[o][1], x, md + 1, rr);
	}
	LL query_sumV(int i, int o, int x, int ll, int rr) {
		if(ll == rr) return sumV[i] - sumV[o];
		if(x > md) return query_sumV(ch[i][1], ch[o][1], x, md + 1, rr);
		return sumV[ch[i][1]] - sumV[ch[o][1]] + query_sumV(ch[i][0], ch[o][0], x, ll, md);
	}
	LL query(int l, int r, int T) {
		int TT = upper_bound(san + 1, san + cnt + 1, T) - san;
		--TT;
		LL ret = 0;
		if(TT > 0) ret += query_sum(rt[r], rt[l-1], TT, 1, cnt);
		if(TT < cnt) ret += query_sumV(rt[r], rt[l-1], TT + 1, 1, cnt) * T;
		return ret;
	}


}T1, T2;
int t[N], L[N], R[N];
LL initSum[N];

int q;
int pre[N], ch[N][2], key[N], lazy[N];
int nodeCnt[N];

int findRoot(int u) {
	while(pre[u]) u = pre[u];
	return u;
}
int findL(int u) {
	while(ch[u][0]) u = ch[u][0];
	return u;
}
int findR(int u) {
	while(ch[u][1]) u = ch[u][1];
	return u;
}

int findK(int u, int k) {
	while(1) {
		if(nodeCnt[ch[u][0]] >= k) u = ch[u][0];
		else {
			k -= nodeCnt[ch[u][0]] + 1;
			if(k == 0) return u;
			u = ch[u][1];
		}
	}
	return -1;
}

void push_up(int x) {
	nodeCnt[x] = nodeCnt[ch[x][0]] + nodeCnt[ch[x][1]] + 1;
}

void rot(int x) {
	int y = pre[x], d = ch[y][1] == x;
	ch[y][d] = ch[x][!d];
	pre[ch[x][!d]] = y;
	ch[x][!d] = y;
	pre[x] = pre[y];
	pre[y] = x;
	if(pre[x]) ch[pre[x]][ch[pre[x]][1]==y] = x;
	push_up(y);
}

void update(int x, int t) {
	if(!x) return;
	key[x] = lazy[x] = t;
}

void push_down(int x) {
	if(lazy[x]) {
		update(ch[x][0], lazy[x]);
		update(ch[x][1], lazy[x]);
		lazy[x] = 0;
	}
}
void P(int x) {
	if(pre[x]) P(pre[x]);
	push_down(x);
}

void splay(int x, int goal) {
	P(x);
	while(pre[x] != goal) {
		int f = pre[x], ff = pre[f];
		if(ff == goal) {
			rot(x);
		}
		else if((ch[ff][1] == f) == (ch[f][1] == x))
			rot(f), rot(x);
		else rot(x), rot(x);
	}
	push_up(x);
}

int split(int u, int k, int t) {
	int p = findK(u, k);
	splay(p, 0);
	int y = ch[p][1];
	pre[ch[p][1]] = 0;
	ch[p][1] = 0;
	push_up(p);
	if(t == 0) return p;	
	return y;
}
	

int merge(int x, int y) {
	x = findRoot(x);
	int t = findR(x);
	y = findRoot(y);
	splay(t, 0);
	ch[t][1] = y;
	pre[y] = t;
	push_up(t);
	return t;
}
bool check(int x) {
	int l = findL(x);
	int r = findR(x);
	if(nodeCnt[x] != r - l + 1) return 0;
	return 1;
}
int buildSplay(int ll, int rr, int p) {
	if(ll > rr) return 0;
	int x = (ll + rr) / 2;
	ch[x][0] = buildSplay(ll, x - 1, x);
	ch[x][1] = buildSplay(x + 1, rr, x);
	pre[x] = p;
	push_up(x);
	return x;
}

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) {
		int c, d;
		scanf("%d%d%d", &c, &d, &v[i]);
		initSum[i] = initSum[i-1] + c;
		T1.mx[i] = d - c;
		T2.mx[i] = d;
	}
	T1.init();
	T2.init();
	scanf("%d", &q);
	for(int i = 1; i <= q; ++i) {
		scanf("%d%d%d", &t[i], &L[i], &R[i]);
	}
	T1.lisan();
	T2.lisan();
	for(int i = 1; i <= n; ++i) nodeCnt[i] = 1;

	buildSplay(1, n, 0);
	for(int i = 1; i <= q; ++i) {
		LL ans = 0;
		for(int j = L[i], x; j <= R[i]; j = x + 1) {
			int p = findRoot(j);
			int l = findL(p);
			int r = findR(p);
			if(nodeCnt[p] != r - l + 1) {
				printf(" error %d %d %d\n", nodeCnt[p], l, r);
				return 0;
			}
			if(l < L[i]) {
				p = split(p, L[i] - l, 1);
				
			}
			if(r > R[i]) {
				p = split(p, nodeCnt[p] - (r - R[i]), 0);
			}
			l = findL(p);
			r = findR(p);
			x = r;
			if(key[p] == 0) {
				ans += T1.query(l, r, t[i] - key[p]);
				ans += initSum[r] - initSum[l-1];
			}
			else {
				ans += T2.query(l, r, t[i] - key[p]);
			}
			if(l != L[i]) p = merge(l - 1, p);
			key[p] = lazy[p] = t[i];
		}
		printf("%lld\n", ans);
	}
	return 0;
}




		


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值