Comet OJ(Contest #8)-D菜菜种菜【树状数组,指针】

前言

话说昨晚写题的时候贼 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的权值有如下要求

  1. 包含点 x x x
  2. l i < L l_i<L li<L R < r i R<r_i R<ri

那么我们可以大致将一个点 x x x的权值分为两个区间 l i + 1 , x l_i+1,x li+1,x x , r i − 1 x,r_i-1 x,ri1。那么只要左端点在左区间,右端点在右区间那么就可以获得这个点的权值。

那么我们将询问区间按照 R R R以升序排序,然后扫到一个右区间的左端点就将左区间整个加上对应点的权值,扫到一个右区间的右端点就将左区间减去对应的权值即可。然后每次取 L L L位置的值就好了。我们用树状数组进行维护即可。


c o d e code code

#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);
}

你可能感兴趣的:(数据结构)