2020牛客暑期多校训练营(第三场)H Sort the Strings Revision 题解

题意:

原串为长度为n(2e6)的 012345678901……

每次将p[i]位置修改为d[i]形成一个新的串,然后继续在新的串上操作,共n次。

求总共n+1个串的排名

这道题上来一看毫无头绪,考场上也就没细想……

这道题有一个很美妙的性质,就是p[i]是一个排列,这就意味着每个位置只会改变一次,那么我们可以p[i1]=1,p[i2]=2,p[i3]=3……的顺序对这些序列进行处理。

所以我们采用类似快速排序的方式,找到当前区间p[i]的最小值,看d[i]与p[i]%10的关系,如果d[i]=p[i]%10,说明没变,跳过,否则如果d[i]<p[i]%10,则所有比 i 大的串字典序都比 i 小的串字典序小,反之亦然。

一开始我找区间最小值的时候用的是线段是,脸黑,T最后5%,改成了笛卡尔树才过……

值得注意的是,在操作完后排名可能会有重复的,这时候我们需要采用类似后缀数组中的桶排的方式以第一次排序结果为第一关键字,以位置为第二关键字进行排序。

  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cmath>
  7 #define N 2000005
  8 using namespace std;
  9 int T,n,ps,pa,pb,pm,p[N];
 10 int ds,da,db,dm,d[N];
 11 struct no{
 12     int left,right,mid;
 13     int mn;
 14 }node[N*4];
 15 void build(int left,int right,int x)
 16 {
 17     node[x].left=left,node[x].right=right;
 18     if(left==right)
 19     {
 20         node[x].mn=left;
 21         return;
 22     }
 23     int mid=(left+right)>>1;
 24     node[x].mid=mid;
 25     build(left,mid,x<<1);
 26     build(mid+1,right,x<<1|1);
 27     if(p[node[x<<1].mn]<p[node[x<<1|1].mn]) node[x].mn=node[x<<1].mn;
 28     else node[x].mn=node[x<<1|1].mn;
 29 }
 30 int Mn;
 31 inline void get(int left,int right,int x)
 32 {
 33     if(node[x].left==left&&node[x].right==right)   
 34     {
 35         if(p[node[x].mn]<p[Mn])
 36         {
 37             Mn=node[x].mn;
 38         }
 39         return;
 40     }
 41     int mid=node[x].mid;
 42     if(left>mid) get(left,right,x<<1|1);
 43     else if(right<=mid) get(left,right,x<<1);
 44     else get(left,mid,x<<1),get(mid+1,right,x<<1|1);
 45 }
 46 int rnk[N],ls[N],rs[N];
 47 void solve(int left,int right,int tmp)
 48 {
 49     if(right<=left)return;
 50     if(p[tmp]==N)return;
 51     if(p[tmp]%10>d[tmp])
 52     {
 53         rnk[left]+=right-tmp;
 54         rnk[tmp+1]-=right-tmp;
 55     }
 56     else
 57     {
 58         rnk[tmp+1]+=tmp-left+1;
 59         rnk[right+1]-=tmp-left+1;
 60     }
 61     solve(left,tmp,ls[tmp]); solve(tmp+1,right,rs[tmp]);
 62 }
 63 int num[N],Sa[N],st[N],top,rt;
 64 int main()
 65 {
 66     scanf("%d",&T);
 67     while(T--)
 68     {
 69         scanf("%d",&n);
 70         scanf("%d%d%d%d",&ps,&pa,&pb,&pm);
 71         for(register int i=0;i<n;++i) p[i]=i;
 72         for(register int i=1;i<=n-1;++i)
 73         {
 74             swap(p[i],p[ps%(i+1)]);
 75             ps=1ll*(1ll*ps*pa+1ll*pb)%pm;
 76         }
 77          
 78         scanf("%d%d%d%d",&ds,&da,&db,&dm);
 79         for(register int i=0;i<n;++i)
 80         {
 81             d[i]=ds%10;
 82             ds=1ll*(1ll*ds*da+db)%dm;
 83         }
 84      
 85         for(register int i=0;i<n;++i)
 86         {
 87             if(p[i]%10==d[i]) p[i]=N;
 88             rnk[i]=0;
 89         }
 90          
 91         rnk[n]=0;
 92         p[n+1]=N+5;
 93         rt=0,top=0;
 94         for(int i=0;i<n;i++)
 95         {
 96             if(p[i]<p[rt]) rt=i;
 97             int k=top;
 98             while(top&&p[st[top]]>p[i]) top--;
 99             if(top) rs[st[top]]=i;
100             if(top<k) ls[i]=st[top+1];
101             top++;
102             st[top]=i;
103         }
104         solve(0,n,rt);
105         for(register int i=0;i<=n;++i) num[i]=0;
106         for(register int i=1;i<=n;++i)rnk[i]+=rnk[i-1];
107         for(register int i=0;i<=n;++i) num[rnk[i]]++;
108         for(register int i=1,m=n+2;i<=m;++i) num[i]+=num[i-1];
109         for(register int i=n;i>=0;--i)
110         {
111             Sa[--num[rnk[i]]]=i;
112         }
113         for(register int i=0;i<=n;++i) rnk[Sa[i]]=i;
114         long long ans=0,xp=1;
115         for(register int i=0;i<=n;++i)
116         {
117             ans=(ans+1ll*rnk[i]*xp%1000000007)%1000000007;
118             xp=xp*10000019%1000000007;
119         }
120         printf("%lld\n",ans);
121     }
122     return 0;
123 }
View Code
posted @ 2020-07-22 20:05  Hzoi_joker  阅读(244)  评论(0编辑  收藏  举报