话说昨晚写题的时候贼 N M NM NM惊险,最后22秒把程序交了上去竟然过了
题目链接:https://cometoj.com/contest/58/problem/D?problem_id=2758
n n n个点 m m m条单向边,然后每次询问一个区间 [ L , R ] [L,R] [L,R]求若只选择这个区间的点,求所有不能直接到达其他任何点的点权之和。
对于每个点我们一定可以确定一个区间 [ l i , r i ] [l_i,r_i] [li,ri]表示若选择了这个区间以外的就无法获得这个点的权值。
那么我们对于询问区间 [ L , R ] [L,R] [L,R]可以获得点 x x x的权值有如下要求
那么我们可以大致将一个点 x x x的权值分为两个区间 l i + 1 , x l_i+1,x li+1,x和 x , r i − 1 x,r_i-1 x,ri−1。那么只要左端点在左区间,右端点在右区间那么就可以获得这个点的权值。
那么我们将询问区间按照 R R R以升序排序,然后扫到一个右区间的左端点就将左区间整个加上对应点的权值,扫到一个右区间的右端点就将左区间减去对应的权值即可。然后每次取 L L L位置的值就好了。我们用树状数组进行维护即可。
#include
#include
#include
#include
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);
}