Comet OJ - Contest #8 D 菜菜种菜

传送门

官方题解 (讲的很好)

解法:我们将所有土地排成一行,可以很好的转化为区间问题,每次询问一个区间 [L,R] 中符合条件土地的菜值和。想要一个土地符合条件,它能够到达的土地必须在询问区间之外或者不存在。意思就是它左边最近能够到达的土地 x ( 满足条件 x<L ,不存在可以到达的土地 x=0 ),同理它右边最近能够到达的土地 y (满足条件 y>R ,不存在可以到达的土地 y=n+1 )。
上面的条件可以转化为一个三维数点的问题,满足L \leqslant i \leqslant R,x_{i}< L,y_{i}> R这些条件土地的菜值和。

我们记作 S(a,b,c,d,e,f) 为满足 a\leqslant i\leqslant b,c \leqslant x_{i} \leqslant d,e \leqslant y_{i} \leqslant f 的土地菜值之和。
答案我们看为 S(L,R,0,L-1,R+1,n+1)

利用容斥原理(类似二维前缀和求矩形面积)

S(L,R,0,L-1,R+1,n+1)=S(L,R,0,n+1,0,n+1)-S(L,R,L,n+1,0,n+1)-S(L,R,0,n+1,0,R)+S(L,R,L,n+1,0,R)

式子第一项 S(L,R,0,n+1,0,n+1) 是一个区间和(前缀和搞定)

式子第二项S(L,R,L,n+1,0,n+1) 只需要满足 i \leqslant R,x_{i}>=L 。

式子第三项S(L,R,0,n+1,0,R) 只需要满足 y_{i} \leqslant R,i>=L 。

式子第四项S(L,R,L,n+1,0,R) 只需要满足 y_{i} \leqslant R,x_{i}>=L 。

可以发现将三维数点问题转化为三个二维数点问题,将所有询问离线,点和询问按第一维排序,第二维作为 下标,顺次扫描,遇到一个点将它第二维对应的位置加上它的权值,遇到一个 询问就查询对应第二维的对应的区间和,只需一个支持单点加,区间求和的数据结构,树状数组即可胜任。

代码:

#include<bits/stdc++.h>
#define lowbit(x)   (x&(-x))
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int read() {
    int x = 0, w = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'), ch = getchar(); }
    return x * w;
}
int n, m, q;
int x[maxn], y[maxn], a[maxn], pre[maxn];
int ans[maxn];
ll low[maxn];
struct node {
    int l, r, id, add;
} Q[maxn], temp[maxn << 1];
bool cmp(node a, node b) { return a.l == b.l ? a.id < b.id : a.l > b.l; }
void add(int i, int add) {
    if (i == 0) return;
    while (i <= n) { low[i] += add, i += lowbit(i); }
}
ll sum(int i) {
    ll all = 0;
    while (i > 0) { all += low[i], i -= lowbit(i); }
    return all;
}
/// L<=x[i],i<=R
void solve_1() {
    for (int i = 0; i <= n + 1; i++)low[i] = 0;
    for (int i = 1; i <= q; i++)temp[i] = Q[i];
    for (int i = 1; i <= n; i++)temp[i + q] = node{x[i], i, 0, a[i]};
    sort(temp + 1, temp + 1 + n + q, cmp);
    for (int i = 1; i <= n + q; i++) {
        if (temp[i].id)ans[temp[i].id] -= sum(temp[i].r);
        else add(temp[i].r, temp[i].add);
    }
}
/// L<=i y[i]<=R
void solve_2() {
    for (int i = 0; i <= n + 1; i++)low[i] = 0;
    for (int i = 1; i <= q; i++)temp[i] = Q[i];
    for (int i = 1; i <= n; i++)temp[i + q] = node{i, y[i], 0, a[i]};
    sort(temp + 1, temp + 1 + n + q, cmp);
    for (int i = 1; i <= n + q; i++) {
        if (temp[i].id)ans[temp[i].id] -= sum(temp[i].r);
        else add(temp[i].r, temp[i].add);
    }
}
///L<=x[i] y[i]<=R
void solve_3() {
    for (int i = 0; i <= n + 1; i++)low[i] = 0;
    for (int i = 1; i <= q; i++)temp[i] = Q[i];
    for (int i = 1; i <= n; i++)temp[i + q] = node{x[i], y[i], 0, a[i]};
    sort(temp + 1, temp + 1 + n + q, cmp);
    for (int i = 1; i <= n + q; i++) {
        if (temp[i].id)ans[temp[i].id] += sum(temp[i].r);
        else add(temp[i].r, temp[i].add);
    }
}
int main() {
    n = read(), m = read(), q = read();
    for (int i = 1; i <= n; i++) {
        a[i] = read();
        pre[i] = pre[i - 1] + a[i];
        x[i] = 0, y[i] = n + 1;
    }
    int u, v;
    for (int i = 1; i <= m; i++) {
        u = read(), v = read();
        if (u > v)x[u] = max(x[u], v);
        else y[u] = min(y[u], v);
    }
    for (int i = 1; i <= q; i++) {
        Q[i].l = read();
        Q[i].r = read();
        ans[i] = pre[Q[i].r] - pre[Q[i].l - 1];
        Q[i].id = i;
    }
    solve_1(), solve_2(), solve_3();
    ll all = 0;
    for (int i = 1; i <= q; i++) all = all ^ (1LL * ans[i] * i);
    printf("%lld\n", all);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值