P3168 [CQOI2015]任务查询系统
求前 $K$ 小的数的和,考虑主席树
但是如果每个时间都暴力插入显然会GG
发现每个任务都是区间,查询是单点查询
所以考虑维护差分数组
直接用主席树维护差分数组,因为同一时间差分可能有多次修改,所以要把当前修改全部搞完才算当前时间的线段树
询问就在相应时间点的线段树上走
具体看代码理解吧
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> using namespace std; typedef long long ll; inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e5+7,M=2e7+7; int n,m; int rt[N],L[M],R[M],sz[M],cnt; ll S[M];//注意long long! int val,K; ll res;//long long ! void ins(int &o,int l,int r,int pre)//插入 { o=++cnt; S[o]=S[pre]+val; sz[o]=sz[pre]+(val>0 ? 1 : -1); //注意我们维护的是差分数组,要根据val的正负来判断加还是删 if(l==r) return; int mid=l+r>>1; if(abs(val)<=mid) ins(L[o],l,mid,L[pre]),R[o]=R[pre];//注意val要取绝对值 else ins(R[o],mid+1,r,R[pre]),L[o]=L[pre]; } void query(int o,int l,int r) { if(l==r) { res+=l*K; return; } int mid=l+r>>1; if(sz[L[o]]<K) { K-=sz[L[o]]; res+=S[L[o]]; query(R[o],mid+1,r); } //注意当前的位置是[1,pos]为闭区间所以是sz[L[o]] '<' K不是 '<=',(upd:好像也可以,但是最好还是注意一下变量的意义qwq) else query(L[o],l,mid); } int p[N],tot; vector <int> V[N];//维护一个时间点的差分操作 int main() { int a,b,c,mx=0; n=read(),m=read(); for(int i=1;i<=n;i++) { a=read(),b=read(),c=read(); mx=max(mx,c); V[a].push_back(c); V[b+1].push_back(-c); } for(int i=1;i<=n;i++) { for(int j=V[i].size()-1;j>=0;j--)//要先处理所有操作 tot++,val=V[i][j],ins(rt[tot],1,mx,rt[tot-1]); p[i]=tot;//最后才算当前时间点的线段树 } int x; ll las=1;//初始las=1,long long ! while(m--) { x=read(),a=read(),b=read(),c=read(); x=rt[p[x]]; K=1+(las*a+b)%c; if(sz[x]<=K) { printf("%lld\n",S[x]); las=S[x]; continue; }//特判 res=0; query(x,1,mx); printf("%lld\n",res); las=res;//输出要long long! } }