BZOJ3932: [CQOI2015]任务查询系统
要询问前K小的值之和,并且每个任务都是区间操作,单点修改,为了实现这个操作,我们可以利用差分数组进行实现。。。
在开始时刻a的位置上,加上任务的优先级,在b+1时刻的位置上,减去任务的优先级,再用主席树维护前缀和。。。然后就没有了
#include<iostream> #include<string.h> #include<stdio.h> #include<algorithm> #include<vector> #define LL long long #define rep(i,j,k) for(int i=j;i<=k;i++) #define per(i,j,k) for(int i=j;i>=k;i--) #define LL long long #define pb push_back using namespace std; const int N = 2e5+7,M=2e7+7; struct node{ int l,r,sum; LL val; }tree[M]; vector<int>v[N]; int tot,cnt,n,m; int root[N],p[N]; void inserts(int pre,int &now,int l,int r,int val){ now=++cnt; ///更新节点编号 tree[now]=tree[pre]; if (val>0)tree[now].sum++; ///如果当前是加入新的任务 else tree[now].sum--; ///如果当前是把任务取消操作 tree[now].val+=val; ///记录一个前缀和 if(l==r){ return; } int mid=(l+r)>>1; if(abs(val)<=mid){ inserts(tree[pre].l,tree[now].l,l,mid,val); }else{ inserts(tree[pre].r,tree[now].r,mid+1,r,val); } } LL query(int rt,int l,int r,int k){ if (l==r){ return (LL)l*k; } int mid=(l+r)>>1; ///如果树的左儿子节点的任务个树是大于等于K个,直接查询左子树 ///反之查询右子树 if(tree[tree[rt].l].sum>=k){ return query(tree[rt].l,l,mid,k); }else { return tree[tree[rt].l].val+query(tree[rt].r,mid+1,r,k-tree[tree[rt].l].sum); } } int main(){ int a,b,c,mx=0; while(~scanf("%d%d",&n,&m)){ tot=0; cnt=0; mx=0; rep(i,1,n){ scanf("%d%d%d",&a,&b,&c); mx=max(mx,c); ///在a时间点上执行+c操作 v[a].pb(c); ///在b+1时间点上执行-c操作 v[b+1].pb(-c); ///v其实存储的就是每个时间点的操作 }; rep(i,1,n){ for(int j=v[i].size()-1;j>=0;j--){ tot++; inserts(root[tot-1],root[tot],1,mx,v[i][j]);///需要把每个时间点的操作进行更新 } p[i]=tot;///每个时间点处理完成后,那么最后编号才是树根 } int x; LL las=1,K; while(m--) { scanf("%d%d%d%d",&x,&a,&b,&c); x=root[p[x]]; K=1+(las*a+b)%c; ///如果里面的个数不足K个 if(tree[x].sum<=K){ printf("%lld\n",tree[x].val); las=tree[x].val; }else{ las=query(x,1,mx,K); printf("%lld\n",las); } } } return 0; }
有不懂欢迎咨询
QQ:1326487164(添加时记得备注)