2020牛客暑期多校训练营(第三场)Sort the Strings Revision 笛卡尔树

题意

有一个长度为\(n\)的字符串\(s_0\), 第\(i\) 个字符是数字 \(i~mod~10\), 给定一 个\(0 \sim n-1\)的排列\(p_i\)和 序列\(d_i\),接着还有\(n\)个字串\(s_1 \sim s_n\), \(s_i\) 是把 \(s_{i-1}\) 的第\(p_i\)个字符改成\(d_i\)的结果。请把这\(n+1\)个字符串按字典序排序后,输出每个字符串在排序后的位置。

分析

考虑以区间\(p_i\)的最小值为分界点来分治,设\(rk_i\)为字符串\(s_i\)的排序后的位置,初始都设为\(0\),若\(p_i\)\(p_0 \sim p_{n-1}\)的最小值,且\(p_i~mod~10 \ne d_i\),有两种情况:

  • \(p_i~mod~10<d_i\),字符串\(s_0\sim s_i\)的字典序就确定小于字符串\(s_{i+1} \sim s_{n}\)了,\(rk_{j,0\le j\le i}+=n-i\)
  • \(p_i~mod~10>d_i\),字符串\(s_0\sim s_i\)的字典序就确定大于字符串\(s_{i+1} \sim s_{n}\)了,\(rk_{j,i+1\le j \le n}+=i+1\)

这样我们就可以把字符串分为两组,再递归处理下去。

这样得到的\(rk_i\)可能会有重复值,用桶排序再搞一遍,得到真正的\(rk_i\)

\(p_i\)建笛卡尔树,就能\(O(N)\)以最小值为分界点来分治(试了下用线段树查区间最小值来分治也能莽过..)。

Code

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=2e6+10;
const int inf=1e9;
int t,n,p_seed,p_a,p_b,p_mod,d_seed,d_a,d_b,d_mod;
int p[N],d[N],rk[N],num[N],sa[N],tot;
int st[N];
int ls[N],rs[N],rt;
void dfs(int pos,int l,int r){
    if(l>=r) return;
    if(p[pos]==inf) return;
    if(p[pos]%10>d[pos]){
    	rk[l]+=r-pos;
    	rk[pos+1]-=r-pos;
    }else{
    	rk[pos+1]+=pos-l+1;
    	rk[r+1]-=pos-l+1;
    }
    dfs(ls[pos],l,pos);dfs(rs[pos],pos+1,r);
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        scanf("%d%d%d%d",&p_seed,&p_a,&p_b,&p_mod);
        scanf("%d%d%d%d",&d_seed,&d_a,&d_b,&d_mod);
        for(int i=0;i<n;i++) p[i]=i;
        for(int i=1;i<n;i++){
            swap(p[p_seed%(i+1)],p[i]);
            p_seed=(1ll*p_seed*p_a+1ll*p_b)%p_mod;
        }
        for(int i=0;i<n;i++){
            d[i]=d_seed%10;
            d_seed=(1ll*d_seed*d_a+1ll*d_b)%d_mod;
        }
        for(int i=0;i<n;i++) if(p[i]%10==d[i]) p[i]=inf;
        int now=0;
    	rt=0;
    	for(int i=0;i<n;i++){
    		if(p[i]<p[rt]) rt=i;
    		int k=now;
    		while(now&&p[st[now]]>p[i]) --now;
    		if(now) rs[st[now]]=i;
    		if(now<k) ls[i]=st[now+1];
    		st[++now]=i;
    	}
        rep(i,0,n) rk[i]=0;
        dfs(rt,0,n);
        for(int i=1;i<=n;i++) rk[i]+=rk[i-1];
        int m=n+5;
        rep(i,0,m) num[i]=0;
        for(int i=0;i<=n;i++) num[rk[i]]++;
        for(int i=1;i<=m;i++) num[i]+=num[i-1];
        for(int i=n;i>=0;i--) sa[--num[rk[i]]]=i;
        rep(i,0,n) rk[sa[i]]=i;
        ll ans=0,ret=1;
        rep(i,0,n){
            ans=(ans+1ll*rk[i]*ret%mod)%mod;
            ret=ret*10000019%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2020-07-20 00:13  xyq0220  阅读(286)  评论(0编辑  收藏  举报