题目描述
最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行),其优先级为Pi。同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同。调度系统会经常向查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个)的优先级之和是多少。特别的,如果Ki大于第Xi秒正在运行的任务总数,则直接回答第Xi秒正在运行的任务优先级之和。上述所有参数均为整数,时间的范围在1到n之间(包含1和n)。
输入
输入文件第一行包含两个空格分开的正整数m和n,分别表示任务总数和时间范围。接下来m行,每行包含三个空格分开的正整数Si、Ei和Pi(Si<=Ei),描述一个任务。接下来n行,每行包含四个空格分开的整数Xi、Ai、Bi和Ci,描述一次查询。查询的参数Ki需要由公式 Ki=1+(Ai*Pre+Bi) mod Ci计算得到。其中Pre表示上一次查询的结果,对于第一次查询,Pre=1。
输出
输出共n行,每行一个整数,表示查询结果。
样例输入
4 3
1 2 6
2 3 3
1 3 2
3 3 4
3 1 3 2
1 1 3 4
2 2 4 3
样例输出
2
8
11
样例解释
K1 = (1*1+3)%2+1 = 1
K2 = (1*2+3)%4+1 = 2
K3 = (2*8+4)%3+1 = 3
对于100%的数据,1<=m,n,Si,Ei,Ci<=100000,0<=Ai,Bi<=100000,1<=Pi<=10000000,Xi为1到n的一个排列
题解
啊……为什么一道主席树的裸题我一定要用可持久化 Treap 做……整个人都要吐了……然而还是毅然决然写了这一道……
看到某些大佬的代码 Insert 和 Delete 操作和我的不一样,要比我的复制更少的节点,跑得比我的快很多,有点虚……
依然是传统的非旋转
Treap
,但是需要可持久化就要复制更多的节点了……和主席树一个道理,我们将修改操作变成复制节点,然后在新的节点上乱搞修改即可。这个操作影响的函数其实只有
Split
和
Merge
(当然由于我自己写了一个
Erase
删除一棵树中的最大节点,所以这个函数也要对被删的链进行复制)
回到这一道题,我们把一个任务开始看做插入,结束看做删除,这样询问就变成了找第
x
秒时最小的
然后就是调板了,慢慢调吧,总会 AC 的……
PS :如果你被卡常( TLE 90% 什么的……)了,我提供你 5 种解决方案
尝试传统卡常方式,比如写读入优化或者疯狂加
inline 或者把递归换成非递归……
推荐指数:★★★☆尝试换一个随机数种子,比如 23333 , 5201314 , 0x3f3f3f3f , INT_MAX
推荐指数:☆尝试像我一样用一个更加科学一点的方式——更换 key 值比较方式。在可持久化的情况下, key 只有在 Merge 函数中会被使用,而且每当使用 Merge ,我们都会复制出一条新的链,我们大可不必保留原来的结构(反正原来的链又没有被删除掉),所以我们此时根据一个基于 size 的随机数,这个随机数在平均情况下有 size(lt)size(lt)+size(rt) 的概率选择左子树( lt ), size(rt)size(lt)+size(rt) 的概率选择右子树( rt ),可以说是很公平了,会比普通的 rand 更快一些(还有你也可以自己写一个 rand 函数,不就是一个奇葩一点的递推式 xi=(xi−1∗a+b)%c 吗,用点吉祥的数字不就卡过去了?)
推荐指数:★★★★这个方法我没有使用,但是绝对比前面的更加可靠,就是向某些大佬学习写一个 Copy 次数更少的 Treap (本人正在尝试中……),就完全不需要卡常了……
推荐指数:★★★★★呃……实在不行你也可以写一发主席树 23333 ……
推荐指数:☆代码
#include<cstdio> #include<cstring> #include<cstdlib> #include<climits> #include<algorithm> using namespace std; #define N 100005 #define lim 100 #define ll long long int root[N]; int st=20191629; inline int ran() { return st=(st*945091459+23333)&0x7f7f7f7f; } class Treap { public: inline void Insert(int &r,int x) { int A,B,C,p=Rank(r,x); Split(r,A,C,p); B=New(x); Merge(r,A,B); Merge(r,r,C); } inline void Delete(int &r,int x) { int A,B,p=Rank(r,x)+1; Split(r,A,B,p); Erase(A); Merge(r,A,B); } int Rank(int r,int a) { if(!r)return 0; if(val[r]<a)return siz[ch[r][0]]+1+Rank(ch[r][1],a); return Rank(ch[r][0],a); } int Query(int r,int x) { if(!r)return 0; if(siz[ch[r][0]]+1<=x) return sum[ch[r][0]]+val[r]+Query(ch[r][1],x-siz[ch[r][0]]-1); return Query(ch[r][0],x); } private: int cnt,ch[N*lim][2],siz[N*lim]; ll val[N*lim],sum[N*lim]; void Pushup(int r) { siz[r]=siz[ch[r][0]]+siz[ch[r][1]]+1; sum[r]=sum[ch[r][0]]+sum[ch[r][1]]+val[r]; } int New(int x) { ++cnt; siz[cnt]=1,val[cnt]=sum[cnt]=x; return cnt; } int Copy(int x) { ++cnt; ch[cnt][0]=ch[x][0]; ch[cnt][1]=ch[x][1]; siz[cnt]=siz[x],val[cnt]=val[x],sum[cnt]=sum[x]; return cnt; } void Split(int r,int <,int &rt,int k)//按排名切割 { if(!r){lt=rt=0;return;} int w=Copy(r); if(siz[ch[r][0]]>=k)rt=w,Split(ch[r][0],lt,ch[rt][0],k); else lt=w,Split(ch[r][1],ch[lt][1],rt,k-siz[ch[r][0]]-1); Pushup(w); } void Merge(int &r,int lt,int rt)//合并 { if(!lt||!rt){r=lt^rt;return;} if(ran_cmp(lt,rt))r=Copy(lt),Merge(ch[r][1],ch[lt][1],rt); else r=Copy(rt),Merge(ch[r][0],lt,ch[rt][0]); Pushup(r); } void Erase(int &r)//删除最大元素 { r=Copy(r); if(!ch[r][1])r=ch[r][0]; else Erase(ch[r][1]); if(r)Pushup(r); } inline int ran_cmp(int a,int b){return ran()%(siz[a]+siz[b])<siz[a];} }S; int m,n; struct node { ll s; int t,wh; bool operator<(node b)const { if(t!=b.t)return t<b.t; return wh<b.wh; } }A[2*N]; ll getint() { ll p=0;bool f=0; char c=getchar(); while(c<'0'||c>'9')if(c=='-')f=1;else c=getchar(); while(c>='0'&&c<='9')p=p*10+c-'0',c=getchar(); if(f)p=-p; return p; } int main() { m=getint();n=getint(); for(int i=1;i<=m;i++) { int a=getint(),b=getint(),c=getint(); A[2*i-1].t=a;A[2*i-1].wh=1;A[2*i-1].s=c; A[2*i].t=b+1;A[2*i].wh=0;A[2*i].s=c; } sort(A+1,A+2*m+1); int p=1; for(int i=1;i<=n;i++) { root[i]=root[i-1]; for(;p<=2*m&&A[p].t==i;p++) if(A[p].wh)S.Insert(root[i],A[p].s); else S.Delete(root[i],A[p].s); } ll pre=1; for(int i=1;i<=n;i++) { int X=getint(),A=getint(),B=getint(),K=getint(); K=1+(A*pre+B)%K; printf("%lld\n",pre=S.Query(root[X],K)); } }