题目链接: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;
}