前言
话说昨晚写题的时候贼NMNMNM惊险,最后22秒把程序交了上去竟然过了
正题
题目链接:https://cometoj.com/contest/58/problem/D?problem_id=2758
题目大意
nnn个点mmm条单向边,然后每次询问一个区间[L,R][L,R][L,R]求若只选择这个区间的点,求所有不能直接到达其他任何点的点权之和。
解题思路
对于每个点我们一定可以确定一个区间[li,ri][l_i,r_i][li,ri]表示若选择了这个区间以外的就无法获得这个点的权值。
那么我们对于询问区间[L,R][L,R][L,R]可以获得点xxx的权值有如下要求
- 包含点xxx
- li<Ll_i<Lli<L且R<riR<r_iR<ri
那么我们可以大致将一个点xxx的权值分为两个区间li+1,xl_i+1,xli+1,x和x,ri−1x,r_i-1x,ri−1。那么只要左端点在左区间,右端点在右区间那么就可以获得这个点的权值。
那么我们将询问区间按照RRR以升序排序,然后扫到一个右区间的左端点就将左区间整个加上对应点的权值,扫到一个右区间的右端点就将左区间减去对应的权值即可。然后每次取LLL位置的值就好了。我们用树状数组进行维护即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e6+100;
struct seq_node{int l,r,w;
};
struct que_node{int l,r,id;
}q[N];
int n,m,t,ans[N],c[N],l[N],r[N],cxk[N];
long long answer;
vector<seq_node> v[N];
int lowbit(int x)
{return x&(x^(x-1));}
void change(int x,int num)
{int i=x;while(i<=n){c[i]+=num;i+=lowbit(i);}
}
int getsum(int x)
{int sum=0;while (x>0){sum+=c[x];x-=lowbit(x);}return sum;
}
bool cmp(que_node x,que_node y)
{return x.r<y.r;}
int main()
{scanf("%d%d%d",&n,&m,&t);for(int i=1;i<=n;i++)l[i]=0,r[i]=n+1,scanf("%d",&cxk[i]);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);if(y>x) r[x]=min(r[x],y);else l[x]=max(l[x],y);}for(int i=1;i<=t;i++){scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;}for(int i=1;i<=n;i++){v[i].push_back((seq_node){l[i]+1,i,cxk[i]});v[r[i]].push_back((seq_node){l[i]+1,i,-cxk[i]});} sort(q+1,q+1+t,cmp);int L=1;for(int i=1;i<=t;i++){while(L<=q[i].r){for(int j=0;j<v[L].size();j++){change(v[L][j].l,v[L][j].w);change(v[L][j].r+1,-v[L][j].w);}L++;}ans[q[i].id]=getsum(q[i].l);}for(int i=1;i<=t;i++)answer^=(long long)i*ans[i];printf("%lld",answer);
}