BZOJ 3932: [CQOI2015]任务查询系统 | 主席树练习题
题目:
洛谷也能评测
题解:
De了好长时间BUG发现是自己sort前面有一行for没删,气死.
题目询问第x秒时候前k小的P值之和.
朴素想法:
我们可以把P值离散化,然后对于每个时刻建一棵定义域是离散化后P值的线段树
每个节点维护了这个节点代表区间的任务个数和这些任务离散化之前的P值之和,
对于每个在这个时间段的任务,插入,即在p位置单点修改
询问就是类似二叉查找树的写法
高级想法:
首先把一段任务拆成两个:添加和删除,用三元组(t,p,d)表示,d=1表示插入,d=-1表示删除
对于第maxt棵线段树,我们把操作按t值排序之后进行,
这岂不是就变成可以一棵可以访问历史版本的线段树---主席树了!
那么写棵主席树,插入操作按t排序就O**K了
#include<cstdio> #include<algorithm> #include<cstring> typedef long long ll; #define N 200100 #define Nlogn N*20 using namespace std; ll root[Nlogn],n,A,B,C,pre=1,m,b[N],K,pcnt,opcnt,lim; struct node { ll lc,rc,sum,val; }t[Nlogn]; struct Op { ll t,p,d; bool operator < (const Op &a) const{return t<a.t;} }op[2*N]; ll read() { ll ret=0,neg=1;char j=getchar(); for (;j>'9' || j<'0';j=getchar()) if (j=='-') neg=-1; for (;j>='0' && j<='9';j=getchar()) ret=ret*10+j-'0'; return ret*neg; } void Insert(ll y,ll &x,ll l,ll r,ll p,ll d) { t[x=++pcnt]=t[y]; t[x].sum+=d;t[x].val+=b[p]*d; if (l==r) return; ll mid=l+r>>1; if (p<=mid) Insert(t[y].lc,t[x].lc,l,mid,p,d); else Insert(t[y].rc,t[x].rc,mid+1,r,p,d); } ll query(ll u,ll l,ll r,ll k) { ll mid,ret=0; while (l<r) { if (t[u].sum==0) return ret; mid=l+r>>1; if (k<=t[t[u].lc].sum) r=mid,u=t[u].lc; else k-=t[t[u].lc].sum,ret+=t[t[u].lc].val,l=mid+1,u=t[u].rc; } return ret+b[l]*k; } int getroot(int x) { return root[x-1]=(root[x-1]!=0 || x==1)?root[x-1]:getroot(x-1); } int main() { m=read(),n=read(); for (ll i=1,s,e,p;i<=m;i++) { s=read(),e=read(),p=read(); op[++opcnt].t=s,b[opcnt]=op[opcnt].p=p,op[opcnt].d=1; op[++opcnt].t=e+1,b[opcnt]=op[opcnt].p=p,op[opcnt].d=-1; } //离散化 sort(b+1,b+1+opcnt); lim=unique(b+1,b+1+opcnt)-b-1; for (ll i=1;i<=opcnt;i++) op[i].p=lower_bound(b+1,b+lim+1,op[i].p)-b; //按操作时间插入节点 sort(op+1,op+1+opcnt); for (ll i=1,j=1;i<=opcnt;i++) { if (root[op[i].t]==0) root[op[i].t]=getroot(op[i].t); Insert(root[op[i].t],root[op[i].t],1,lim+10,op[i].p,op[i].d); } for (ll i=1,x;i<=n;i++) { x=read(),A=read(),B=read(),C=read(); K=1+(A*pre+B)%C; printf("%lld\n",pre=query(root[x],1,lim+10,K)); } return 0; }