2020牛客暑期多校训练营(第三场) H Sort the Strings Revision
思路:由于是字典序,所以肯定是越前面的字符对整体的字典序的影响越大,所以我们先看最小的 \(p[i]\) , 假设最小的 \(p[i] = st\) ,并且 \(d[i] != st \% 10\) 。
如果 \(d[i] < st \% 10\) , 那么 \(s_1 - s_i\) > \(s_{i + 1} - s_n\) ,大于则同理,这个手写一个样例就会理解,这样我们就把所有字符串分成了两个互相独立的字符串
集合,对于每个字符串子集,都可以用同样的办法处理,唯一的难点是要找出连续区间内最小的 \(p[i]\) ,这个可以用笛卡尔树处理,其实也可以线段树,
不过会被卡好像(队友试过)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e6 + 60;
const int MOD = 1000000007;
int n;
int p[maxn], d[maxn], ls[maxn], rs[maxn];
int stk[maxn], top;
void dikaer(){
top = 0;
for(int i = 0; i < n; i++){
if(d[i] == p[i] % 10) {
continue;
}
int k = top;
while(k > 0 && p[stk[k]] > p[i]) k--;
if(k) rs[stk[k]] = i;
if(k < top) ls[i] = stk[k + 1];
stk[++k] = i;
top = k;
}
}
LL pos[maxn];
void dfs(int le, int ri, int st){
if(le == ri) return ;
if(st == -1){
for(int i = 0; i < ri - le + 1; i++){
pos[le + i] += i;
pos[le + i + 1] -= i;
}
return ;
}
if(d[st] > p[st] % 10){
dfs(le, st, ls[st]);
dfs(st + 1, ri, rs[st]);
pos[st + 1] += (st - le + 1);
pos[ri + 1] -= (st - le + 1);
} else if(d[st] < p[st] % 10){
dfs(le, st, ls[st]);
dfs(st + 1, ri, rs[st]);
pos[le] += (ri - st);
pos[st + 1] -= (ri - st);
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
int seed, a, b, mod;
scanf("%d%d%d%d", &seed, &a, &b, &mod);
int nsed = seed;
for(int i = 0; i <= n; i++){
stk[i] = -1;
pos[i] = 0;
ls[i] = rs[i] = -1;
p[i] = i;
}
for(int i = 1; i < n; i++){
swap(p[nsed % (i + 1)], p[i]);
nsed = (1LL * nsed * a + b) % mod;
}
scanf("%d%d%d%d", &seed, &a, &b, &mod);
nsed = seed;
for(int i = 0; i < n; i++){
d[i] = nsed % 10;
nsed = (1LL * nsed * a + b) % mod;
}
dikaer();
dfs(0, n, stk[1]);
for(int i = 1; i <= n; i++){
pos[i] += pos[i - 1];
}
LL x = 1;
LL ans = 0;
for(int i = 0; i <= n; i++){
ans = (1LL * ans + 1LL * pos[i] * x % MOD) % MOD;
x = 10000019LL * x % MOD;
}
printf("%lld\n", ans);
}
return 0;
}