[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;
}