菜菜种菜 Comet OJ - Contest #8

题目链接:https://cometoj.com/contest/58/problem/D?problem_id=2758

对于第i个点,记录他左右距离最近的直接到达点的编号,分别为le[i]ri[i]

当且仅当询问区间[L,R] ,满足 L<=i&&R >=i &&L>le[i]&& R< ri[i] 时第i号节点会产生贡献。 

而 i 号节点左右都有一个失去贡献的端点,不是很好处理。

 

我们称因为R>=ri[i]这种情况为右端点非法, R<ri[i]为右端点合法

我们考虑将询问按照R从小到大排序,开一个priority_queue维护右端点合法的点集。

显然每个点最多只会push一次,pop一次。

这样我们就有了一个右端点一定合法的点集,然后就可以只用考虑左端点导致的贡献丢失问题。

用s[i] 来记录第i号点的点权,让第i个位置加s[i],le[i]位置减s[i],对于每一次询问查询区间和。

含义是:当询问区间包含 i 时,第i号节点产生贡献,当询问区间包含le[i]时失去贡献。

显然:若询问区间包含le[i],那么一定也会包含 i,不会出现未获得贡献直接失去导致结果为负数的情况

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int N = 1e6+100;
int le[N],ri[N],s[N];
ll tree[N];
int lb(int x) {return x&-x;}
void add(int x,int data) {
    if(x==0) return;
    for(int i = x; i < N; i+=lb(i))
        tree[i]+=data;
}
ll query(int x) {
    ll ans = 0;
    for(int i = x; i > 0; i-=lb(i))
        ans += tree[i];
    return ans;
}

struct Q{
    int l,r,id;
    bool operator <(Q b) const {
        return r<b.r;
    }
}que[N];

int main(){
   // freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    int n,m,q;
    cin>>n>>m>>q;
    rep(i, 1, n) {
        cin>>s[i];
        le[i] = 0;
        ri[i] = 1e9;
    }
    rep(i, 1, m) {
        int u,v;
        cin>>u>>v;
        if(v<u) le[u] = max(le[u],v);
        else ri[u] = min(ri[u],v);
    }
    rep(i, 1, q) {
        cin>>que[i].l>>que[i].r;
        que[i].id = i;
    }
    sort(que+1,que+q+1);
    int now = 0;
    priority_queue<pii,vector<pii>,greater<pii> >heap;
    ll ans = 0;
    rep(i, 1, q) {
        rep(j, now+1,que[i].r) {
            heap.push(mp(ri[j],j));
            add(j,s[j]);
            add(le[j],-s[j]);
        }
        now = que[i].r;
        while(!heap.empty()&&heap.top().first<=now) {
            int x = heap.top().second;
            heap.pop();
            add(x,-s[x]);
            add(le[x],+s[x]);
        }
        ans ^= (que[i].id*(query(que[i].r)-query(que[i].l-1)));
    }
    cout<<ans;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值