1. 程式人生 > 實用技巧 >[BJOI2016]回轉壽司

[BJOI2016]回轉壽司

似乎沒有人打平衡樹的題解,那我就來水一發~

我們將題目做一個簡單的轉化:

\(sum_i = \sum_{j=1}^{i} a_j\)

那麼答案就是\(\sum_{i=1}^{n}\sum_{j=1}^{i} (L \leq (sum_i - sum_{j-1}) \leq R)\)

我們可以利用容斥的思想進行簡單是轉化:

\(\sum_{i=1}^{n}\sum_{j=1}^{i} (L \leq (sum_i - sum_{j-1})) - \sum_{i=1}^{n}\sum_{j=1}^{i} (R < (sum_i - sum_{j-1}))\)

因此,我們只用列舉\(sum_i\)

,並在一個可以接受的時間複雜度內分別找到相對應的\(j\)的數目,即\(sum_{j - 1} \leq sum_i - L\)\(j\)的個數和\(sum_{j - 1} \leq sum_i - R - 1\)\(j\)的數量,並把他們相減。

而查詢這些數目完全可以用平衡樹來實現,下面的程式碼打的是\(treap\),僅供參考。

注:題目的資料範圍似乎有問題,似乎是\(0 \leq L , R\leq 10^9\)

#include <cstdio>
#include <ctime>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 5;

struct node {
	LL lc , rc , data , pri , si , cnt;
}t[MAXN];

void Zig(LL &k) {
	LL y = t[k].lc;
	t[k].lc = t[y].rc;
	t[y].rc = k;
	t[y].si = t[k].si;
	t[k].si = t[t[k].lc].si + t[t[k].rc].si + t[k].cnt;
	k = y;
}

void Zag(LL &k) {
	LL y = t[k].rc;
	t[k].rc = t[y].lc;
	t[y].lc = k;
	t[y].si = t[k].si;
	t[k].si = t[t[k].lc].si + t[t[k].rc].si + t[k].cnt;
	k = y;
}

LL num = 0;
void Insert(LL &k , LL key) {
	if(!k) {
		k = ++num;
		t[k].data = key;t[k].pri = rand();
		t[k].si = 1;t[k].cnt = 1;
		return;
	} 
	t[k].si ++;
	if(t[k].data == key) t[k].cnt ++;
	else if(t[k].data < key) {
		Insert(t[k].rc , key);
		if(t[t[k].rc].pri < t[k].pri) Zag(k);
	}
	else {
		Insert(t[k].lc , key);
		if(t[t[k].lc].pri < t[k].pri) Zig(k);
	}
}

LL n , rt;

LL x_rank(LL x) {
	LL k = rt , ans = 0;
	while(k) {
		if(x == t[k].data) return ans + t[t[k].lc].si + t[k].cnt;
		if(x > t[k].data) ans += t[t[k].lc].si + t[k].cnt , k = t[k].rc;
		else k = t[k].lc;
	}
	return ans;
}

LL L , R , a[MAXN];
int main() {
	srand(19491001);
	scanf("%lld %lld %lld" , &n , &L , &R);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld" , &a[i]);
		a[i] += a[i - 1];
	}
	LL ans = 0;
	Insert(rt , 0);
	for (int i = 1; i <= n; ++i) {
		ans += x_rank(a[i] - L);
		ans -= x_rank(a[i] - R - 1); 
		Insert(rt , a[i]);
	}
	printf("%lld" , ans);
	return 0;
}