Sort the Strings Revision
题目描述:
有n+1个长度为n的字符串---- S 1 S_1 S1, S 2 S_2 S2,…, S n S_n Sn。 字符串中的字符从0到n-1(从左到右)编号。字符 S i S_i Si是 i i i模10的数字。例如,如果n=5,则S为“01234”,如果n=14,则S为“ 01234567890123”。给定一个0到n-1的排列: p [ 0 ] p[0] p[0], p [ 1 ] p [1] p[1],…, p [ n − 1 ] p[n-1] p[n−1]和长度为n的数字序列: d [ 0 ] d[0] d[0], d [ 1 ] d [1] d[1],…, d [ n − 1 ] d[n-1] d[n−1]。 S i + 1 S_{i+1} Si+1可以通过用数字 d [ i ] d[i] d[i]代替 S i S_i Si的字符 p [ i ] p[i] p[i]来获得。 请注意,如果 d [ i ] d[i] d[i]与 S i S_i Si的字符 p [ i ] p[i] p[i]相同,则S将与 S i S_i Si相同.现在我们要对这n+1个字符串进行排序。 当且仅当 S i S_i Si在字典上小于 S j S_j Sj或 S i S_i Si等于 S j S_j Sj并且 i i i< j j j时,才应将索引为i的字符串放在索引为j的字符串的左侧。让 r i r_i ri为字符串 S i S_i Si从左开始的新位置, 也就是说, S r i S_{ri} Sri是排序后从左起第 i i i个字符串。(位置是从0开始的数字)例如,如果n=5,p=[4,3,0,1,2]和d=[5,0,0,0,0],则 S 0 S_0 S0=“ 01234”, S 1 S_1 S1=“01235”, S 2 S_2 S2=“01205”, S 3 S_3 S3=“01205”, S 4 S_4 S4=“00205”, S 5 S_5 S5=“00005”。因此 r 0 r_0 r0=4, r 1 r_1 r1=5, r 2 r_2 r2=2, r 3 r_3 r3=3, r 4 r_4 r4=1和 r 5 r_5 r5=0。请求出从0到n的所有的 r i r_i ri
输入描述:
第一行包含一个整数t(1≤t≤10^ 3)表示测试用例的数量。每个测试的第一行包含一个正整数n(1≤n≤2×10^ 6)。每个测试的第二行包含四个整数
p
s
e
e
d
p_{seed}
pseed,
p
a
p_a
pa,
p
b
p_b
pb,
p
m
o
d
p_{mod}
pmod(0≤
p
s
e
e
d
p_{seed}
pseed,
p
a
p_a
pa,
p
b
p_b
pb <
p
m
o
d
p_{mod}
pmod≤10^ 9+7)。 使用以下伪代码生成p。
s
e
e
d
seed
seed
=
=
=
p
s
e
e
d
p_{seed}
pseed
f
o
r
for
for
i
i
i
=
=
=
0
0
0
t
o
to
to
n
−
1
n-1
n−1
:
:
:
p
[
i
]
p[i]
p[i]=
i
i
i
f
o
r
for
for
i
i
i
=
=
=
1
1
1
t
o
to
to
n
−
1
n-1
n−1
{
s
w
a
p
swap
swap
t
h
e
the
the
v
a
l
u
e
value
value
o
f
of
of
p
[
p[
p[
s
e
e
d
seed
seed
m
o
d
u
l
o
modulo
modulo
(
i
+
1
)
(i + 1)
(i+1)
]
]
]
p
[
p[
p[
s
e
e
d
seed
seed
m
o
d
u
l
o
modulo
modulo
(
i
+
1
)
(i+1)
(i+1)
]
]
]
a
n
d
and
and
p
[
i
]
p[i]
p[i]
s
e
e
d
seed
seed
=
=
=
(
(
(
s
e
e
d
seed
seed
∗
*
∗
p
a
p_a
pa
+
+
+
p
b
p_b
pb
)
)
)
m
o
d
u
l
o
modulo
modulo
p
m
o
d
p_{mod}
pmod
}
每个测试的第三行包含四个整数
d
s
e
e
d
d_ {seed}
dseed,
d
a
d_a
da,
d
b
d_b
db,
d
m
o
d
d_ {mod}
dmod(0
≤
\le
≤
d
s
e
e
d
d_ {seed}
dseed,
d
a
d_a
da,
d
b
d_b
db <
d
m
o
d
d_ {mod}
dmod
≤
\le
≤ 10^9+7)。 使用以下伪代码生成d。
s
e
e
d
seed
seed
=
=
=
d
s
e
e
d
d_{seed}
dseed
f
o
r
for
for
i
i
i
=
=
=
0
0
0
t
o
to
to
n
−
1
n-1
n−1
:
:
:
d
[
i
]
d[i]
d[i]
=
=
=
s
e
e
d
seed
seed
m
o
d
u
l
o
modulo
modulo
10
10
10
s
e
e
d
seed
seed
=
=
=
(
(
(
s
e
e
d
seed
seed
∗
*
∗
d
a
d_a
da
+
+
+
d
b
d_b
db
)
)
)
m
o
d
u
l
o
modulo
modulo
d
m
o
d
d_{mod}
dmod
测试用例中n的总和不超过10 ^ 7
输出描述:
对于每个测试,您不需要输出
r
i
r_i
ri的整个序列。 相反,您应该输出一个非负整数
(
(
(
i
=
0
∑
n
i=0∑n
i=0∑n(
r
i
r_i
ri
∗
∗
∗
1000001
9
i
10000019^i
10000019i
)
)
))
))
m
o
d
mod
mod
1000000007
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
pi的最小值可以发现,每次可以通过
p
i
p_i
pi的最小值来将数列划分为两部分,在
1
1
1 ~
p
i
p_i
pi之间和
p
i
+
1
p_{i+1}
pi+1 ~
p
n
p_n
pn之间重复上述过程,在
d
i
d_i
di>
p
i
p_i
pi时字典序变大…这不就是二分吗?
所以这道题可以用二分来解:每次找到最小的
p
i
p_i
pi,将数列分成两部分,再继续递归,直到只有一个为止,最后比较
d
i
d_i
di和
p
i
p_i
pi的大小。
那么问题来了,如何才能快速求出区间内
p
i
p_i
pi的最小值呐?这就需要引进一个知识——笛卡尔树(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);
}
}