2020牛客暑期多校训练营第三场Sort the Strings Revision

Sort the Strings Revision

原题请看这里

题目描述:

有n+1个长度为n的字符串---- S 1 S_1 , S 2 S_2 ,…, S n S_n 。 字符串中的字符从0到n-1(从左到右)编号。字符 S i S_i i i 模10的数字。例如,如果n=5,则S为“01234”,如果n=14,则S为“ 01234567890123”。给定一个0到n-1的排列: p [ 0 ] p[0] , p [ 1 ] p [1] ,…, p [ n 1 ] p[n-1] 和长度为n的数字序列: d [ 0 ] d[0] , d [ 1 ] d [1] ,…, d [ n 1 ] d[n-1] S i + 1 S_{i+1} 可以通过用数字 d [ i ] d[i] 代替 S i S_i 的字符 p [ i ] p[i] 来获得。 请注意,如果 d [ i ] d[i] S i S_i 的字符 p [ i ] p[i] 相同,则S将与 S i S_i 相同.现在我们要对这n+1个字符串进行排序。 当且仅当 S i S_i 在字典上小于 S j S_j S i S_i 等于 S j S_j 并且 i i < j j 时,才应将索引为i的字符串放在索引为j的字符串的左侧。让 r i r_i 为字符串 S i S_i 从左开始的新位置, 也就是说, S r i S_{ri} 是排序后从左起第 i i 个字符串。(位置是从0开始的数字)例如,如果n=5,p=[4,3,0,1,2]和d=[5,0,0,0,0],则 S 0 S_0 =“ 01234”, S 1 S_1 =“01235”, S 2 S_2 =“01205”, S 3 S_3 =“01205”, S 4 S_4 =“00205”, S 5 S_5 =“00005”。因此 r 0 r_0 =4, r 1 r_1 =5, r 2 r_2 =2, r 3 r_3 =3, r 4 r_4 =1和 r 5 r_5 =0。请求出从0到n的所有的 r i r_i

输入描述:

第一行包含一个整数t(1≤t≤10^ 3)表示测试用例的数量。每个测试的第一行包含一个正整数n(1≤n≤2×10^ 6)。每个测试的第二行包含四个整数 p s e e d p_{seed} p a p_a p b p_b p m o d p_{mod} (0≤ p s e e d p_{seed} p a p_a p b p_b < p m o d p_{mod} ≤10^ 9+7)。 使用以下伪代码生成p。
s e e d seed = = p s e e d p_{seed}
f o r for i i = = 0 0 t o to n 1 n-1 : : p [ i ] p[i] = i i
f o r for i i = = 1 1 t o to n 1 n-1
{
s w a p swap t h e the v a l u e value o f of p [ p[ s e e d seed m o d u l o modulo ( i + 1 ) (i + 1) ] ]
p [ p[ s e e d seed m o d u l o modulo ( i + 1 ) (i+1) ] ] a n d and p [ i ] p[i]
s e e d seed = = ( ( s e e d seed * p a p_a + + p b p_b ) ) m o d u l o modulo p m o d p_{mod}
}
每个测试的第三行包含四个整数 d s e e d d_ {seed} d a d_a d b d_b d m o d d_ {mod} (0 \le d s e e d d_ {seed} d a d_a d b d_b < d m o d d_ {mod} \le 10^9+7)。 使用以下伪代码生成d。
s e e d seed = = d s e e d d_{seed}
f o r for i i = = 0 0 t o to n 1 n-1 : :
d [ i ] d[i] = = s e e d seed m o d u l o modulo 10 10
s e e d seed = = ( ( s e e d seed * d a d_a + + d b d_b ) ) m o d u l o modulo d m o d d_{mod}
测试用例中n的总和不超过10 ^ 7

输出描述:

对于每个测试,您不需要输出 r i r_i 的整个序列。 相反,您应该输出一个非负整数
( ( i = 0 n i=0∑n ( r i r_i 1000001 9 i 10000019^i ) ) )) m o d mod 1000000007 1000000007 . .

样例输入:

2
5
1 3 1 4
5 2 0 170
1
0 0 0 1
1000000000 1000000006 1000000006 1000000007

样例输出:

26717147
10000019

思路:

二分+笛卡尔树 数据卡ST表T了好几遍emmmm…
通过观察 p i p_i 的最小值可以发现,每次可以通过 p i p_i 的最小值来将数列划分为两部分,在 1 1 ~ p i p_i 之间和 p i + 1 p_{i+1} ~ p n p_n 之间重复上述过程,在 d i d_i > p i p_i 时字典序变大…这不就是二分吗?
所以这道题可以用二分来解:每次找到最小的 p i p_i ,将数列分成两部分,再继续递归,直到只有一个为止,最后比较 d i d_i p i p_i 的大小。
那么问题来了,如何才能快速求出区间内 p i p_i 的最小值呐?这就需要引进一个知识——笛卡尔树(ST表会T)
笛卡尔树的原理大概是这样的:
用单调栈来维护一个链表,如果违背单调栈,那就放在左子树上(这里就不详细讲了,如有需要请看这里

AC Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int HASH=1e7+19;
const int MAXN=2e6+5;
const ll MAX=INT_MAX+1e8;
const ll mod=1e9+7;
ll Pseed,Pa,Pb,Pmod,P[MAXN],Dseed,Da,Db,Dmod,D[MAXN];
ll st[MAXN],ans,Rank[MAXN],has,l[MAXN],r[MAXN];
int t,n,val;
void cartesian()
{
    int tmp=0,x;
    for(int i=0;i<n;i++)
    {
        x=tmp;
        while(x>0&&P[st[x]]>P[i]) x--;
        if(x) r[st[x]]=i;
        if(x<tmp) l[i]=st[x+1];
        st[++x]=i;
        tmp=x;
    }
}//笛卡尔树
void dfs(int left,int right,int minn)
{
    if(left>right) return;
    if(left==right){Rank[left]=val++;return;}
    if(P[minn]==MAX)
    {
        for(int i=left;i<=right;i++)
            Rank[i]=val++;
        return;
    }
    if(P[minn]%10>D[minn]) dfs(minn+1,right,r[minn]),dfs(left,minn,l[minn]);
    else dfs(left,minn,l[minn]),dfs(minn+1,right,r[minn]);
}//二分
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        has=1;
        val=0;
        ans=0;
        scanf("%d",&n);
        scanf("%lld%lld%lld%lld",&Pseed,&Pa,&Pb,&Pmod);
        scanf("%lld%lld%lld%lld",&Dseed,&Da,&Db,&Dmod);
        for(int i=0;i<n;i++)
        {
            P[i]=i;
            D[i]=Dseed%10;
            Dseed=(Dseed*Da+Db)%Dmod;
        }
        for(int i=1;i<n;i++)
        {
            swap(P[i],P[Pseed%(i+1)]);
            Pseed=(Pseed*Pa+Pb)%Pmod;
        }
        for(int i=0;i<n;i++)
            if(P[i]%10==D[i]) P[i]=MAX;//无用数据
        cartesian();
        dfs(0,n,st[1]);
        for(int i=0;i<=n;i++)
        {
            ans+=(ll)(Rank[i]*has)%mod;
            ans%=mod;
            has*=HASH;
            has%=mod;
        }
        printf("%lld\n",ans%mod);
    }
}

本文参考博客

猜你喜欢

转载自blog.csdn.net/s260127ljy/article/details/107523237