原题
题目描述
有n + 1个长度为n的字符串— s[0],s[1],…,s[n]。字符串中的字符从0到n-1(从左到右)编号。 s0的第i个字符是i取模10后的数字。
例如,如果n = 5,则s[0]为“ 01234”,如果n = 14,则s[0]为“ 01234567890123”。
给定一个0到n-1的排列:p [0],p [1],…,p [n-1]和长度为n的数字序列: d [0],d [1],…,d [n-1]。s[i + 1]可以通过用数字d [i]代替s[i]的第p[i]个字符来获得。注意,如果d [i]与s[i]的第p[i]个字符相同,则s[i + 1]将与s[i]相同。
现在我们要对这n + 1个字符串进行排序。当且仅当s[i]在字典序上小于s[j]或s[i]等于s[j]并且i <j时,才应将索引为i的字符串放在索引为j的字符串的左侧。令r[i]为字符串s[i]从左起的新位置,即S[r[i]是排序后从左数起的第i个字符串。 (位置是从0开始的数字。)
例如,如果n = 5,p = [4,3,0,1,2]和d = [5,0,0,0,0],则
s0 =“01234”,
s1=“01235”,
s2=“01205”,
s3 = “01205”,
s4 ="00205”,
s5 =“00005”。
因此r0 = 4,r1 = 5,r2 = 2,r3 = 3,r4 = 1,r5 = 0。
请为计算i从0到n的所有ri。
样例
输入
2
5
1 3 1 4
5 2 0 170
1
0 0 0 1
1000000000 1000000006 1000000006 1000000007
输出
26717147
10000019
思路
首先我们拿到这道题,如果不先看数据范围,我们肯定能想到用暴力的方法求解这道题。只要先拷贝一下前一个字符串,然后按照出题人的要求做就行了。最后sort排个序,随便输出一下就行了。
但是这道题的范围特别大,所以直接暴力不仅会超时,存不下所有的字符串,所以我们要想另一种方法。我们先来看样例的模拟。
s
0
_0
0=“01234” r
0
_0
0=4
s
1
_1
1=“01235” r
1
_1
1=5
s
2
_2
2=“01205” r
2
_2
2=2
s
3
_3
3=“01205” r
3
_3
3=3
s
4
_4
4="00205” r
4
_4
4=1
s
5
_5
5=“00005” r
5
_5
5=0
从中我们可以看到高位数字的改变所带来字符串顺序的改变往往比低位数字大。
从图中我们可以更加直观地看出,第二位数字的变化直接把整个字符串的顺序劈成了两半。而第三位和第四位上数字的改变只是在原来的基础上再劈。这也就验证了我们上一个得出的结论是正确的。这也就是一个分治的思想。
而且我们会发现,像这样一直递归下去,迟早只会剩下一个字符串,这个时候,它们的大小顺序也就可以确定了。
因此,我们要找在同一个区间内数字被改变的最高位,也就是找到最小的p
i
_i
i。
所以,这道题目就变成了求区间内的最小值。就可以用st表,线段树等做法,但是用这些算法还是会TLE。可能是出题人故意卡掉这些算法的
所以我们还需要一种更快的算法,那就是笛卡尔树。用单调栈或者数组模拟都可以。
代码
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7,maxn=2e6+5,o=0x3f3f3f3f;
int t,n,x,y,z,sum,p[maxn],d[maxn],ans[maxn],s[maxn],st[maxn][2];
long long a1,a2,a3,a4,b1,b2,b3,b4;
void dfs(int left,int right,int rank)//二分
{
if(left>right)return;
if(rank<0){for(int i=left;i<=right;i++)ans[i]=sum++;return;}
if(d[rank]>p[rank]%10)dfs(left,rank,st[rank][0]),dfs(rank+1,right,st[rank][1]);
else dfs(rank+1,right,st[rank][1]),dfs(left,rank,st[rank][0]);
}
int main()
{
for(scanf("%d",&t);t--;x=0,y=-1,sum=0)
{
scanf("%d%lld%lld%lld%lld%lld%lld%lld%lld",&n,&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4);
for(int i=0;i<=n;i++)st[i][0]=st[i][1]=s[i]=-1;
for(int i=0;i<n;i++) p[i]=i;
for(int i=1;i<n;i++)swap(p[a1%(i+1)],p[i]),a1=(a1*a2+a3)%a4;
for(int i=0;i<n;i++)d[i]=b1%10,b1=(b1*b2+b3)%b4;
for(int i=0;i<n;i++)//用数组模拟建树
{
if(p[i]%10==d[i]) continue;
if(x==0 || p[i]>p[s[x]]) x++,s[x]=i;
else
{
z=-1;
while(p[s[x]]>p[i])
{
st[s[x]][1]=z,z=s[x],x--;
if(x==0) break;
}
x++,st[i][0]=z,s[x]=i;
}
}
y=s[1];
for(int i=2;i<=x;i++)st[s[i-1]][1]=s[i];
dfs(0,n,y);
long long v1=1,v2=0;
for(int i=0;i<=n;i++) v2=(v2+ans[i]*v1)%mod,v1=v1*10000019%mod;
printf("%lld\n",v2);
}
return 0;
}