【CQOI2015】任务查询系统 可持久化Treap

题目描述

最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的任务用三元组(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 秒时最小的 k 个元素和,这不就是可持久化 Treap 的拿手好戏吗?我们像查询 Select 一样做,统计答案的时候只要把 +size[lc]+1 变成 +sum[lc]+val[r] 就没有问题了,相安无事,岁月静好……

然后就是调板了,慢慢调吧,总会 AC 的……

PS :如果你被卡常( TLE 90% 什么的……)了,我提供你 5 种解决方案

  1. 尝试传统卡常方式,比如写读入优化或者疯狂加 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=(xi1a+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 &lt,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));
          }
      }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值