Comet OJ Contest #8 D 菜菜种菜 (树状数组)

参考博客:https://blog.csdn.net/nudt_spy/article/details/99055787

大致题意

菜菜太菜,但他不想种菜。
有 n 块土地,每块土地有一个菜值。它们之间有 m 条小路,每条连接两块土地,小路只能单向通行,不存在一条小路两端连接同一块土地,但可能存在两条小路两端连接的土地分别相同。如果存在一条从土地 u 到土地 v 的小路,则称 u 能直接到达 v。菜菜可以购买一些土地,他必须在其中选择一块建造自己的家,所购买的剩下的土地都被作为菜地。因为菜菜不想种菜,所以他希望从他家能直接到达的土地中,一块菜地也没有(如果菜菜家不能直接到达任何一块土地,这也能满足他的愿望)。菜菜提出了 q 个问题,每个问题给出 L,R ,询问如果他购买了第 L到第 R 块之间的所有土地,那么所有满足他愿望的建造家的选址的菜值之和是多少?
另外输出稍微有点要求。

思路

首先要把问题转化一下,对于每个节点,求一个区间[lx,rx],表示在该范围内所有的区间,都符合要求,可以加上a[x]的贡献。显然这个lx是x能到达的最大的比他小的编号,rx是最小的比x大的编号。然后问题就转化成,对每个询问[L,R],求有多少个lx<=L && rx>=R && L<=x<=R
终于看了上面那篇博主写的代码才搞定。因为这个数字都是1-n之间的,所以都不需要离散化了。具体操作是,从1扫到n,每一个i,把所有(lx = = i)的a[x]加到区间[x,rx];然后查看L==i的,查询0-R的和即可。然后为了满足区间要包含x的条件,在扫过i之后,要把a[i]从树状数组种删掉,因为后面的L都大于x了。插入和删除都体现了要L<=x<=R的要求。

代码

#include
using namespace std;
#define maxn 1000005
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;}
int n,m,Q,lx[maxn],rx[maxn],a[maxn],c[maxn],x,y,ans[maxn];
struct node{int r,id;};
vector<node>q[maxn];
vector<int>v[maxn];
void add(int x,int y){while(x<maxn){c[x]+=y;x+=x&(-x);}}
int query(int x){int res=0;while(x){res+=c[x];x-=x&(-x);}return res;}
int main()
{
    memset(rx,INF,sizeof(rx));
    n=read();m=read();Q=read();
    inc(i,1,n)a[i]=read();
    inc(i,1,m){x=read();y=read();if(x<y)rx[x]=min(rx[x],y);else lx[x]=max(lx[x],y);}
    inc(i,1,Q){x=read();y=read();q[x].push_back({y,i});}
    inc(i,1,n)v[lx[i]+1].push_back(i);
    inc(i,1,n){
        for(int j=0;j<v[i].size();j++){add(v[i][j],a[v[i][j]]);add(rx[v[i][j]],-a[v[i][j]]);}
        for(int j=0;j<q[i].size();j++){ans[q[i][j].id]=query(q[i][j].r);}
        add(i,-a[i]);add(rx[i],a[i]);
    }
    ll t=0;
    inc(i,1,Q)t^=1ll*i*ans[i];
    printf("%lld\n",t);
    return 0;
}

你可能感兴趣的:(数据结构-树状数组)