題目鏈接:點擊查看
題目大意:給出一個長度為 n 的數字串 s[ 0 ],每個位置的賦值初始時為 s[ i ] = i % 10 ( i ∈ [ 0 , n - 1 ] ),現在有一個長度為 n 的排列 p,和一個長度為 n 的數列 d ,相當于 n 次操作,每次操作需要將第 p[ i ] 個位置的數字變為 d[ i ] ,這樣一共能得到 n + 1 個數字串,需要給這 n + 1 個數字按照字典序排序
題目分析:顯然是不能構造出 n + 1 個串然后排序的,而且數據范圍也限制了只能 O( n ) 實現,帶個 log 都有可能 T 飛
因為 n 次操作對于每個位置有且僅有一次替換,而且在字典序的排序中,顯然在原數列中位置更靠前的替換的影響要大于位置較靠后的替換,這樣我們可以分治去做:假設當前分治的排列區間是 [ l , r ],設 pos 為 p[ l ] ~ p[ r ] 中的最小值
p[ pos ] % 10 > d[ pos ],替換后數列會變得更小,s[ 0 ] ~ s[ pos ] 的字典序要大于 s[ pos + 1 ] ~ s[ n ] 的字典序 p[ pos ] % 10 < d[ pos ],替換后數列會變得更大,s[ 0 ] ~ s[ pos ] 的字典序要小于 s[ pos + 1 ] ~ s[ n ] 的字典序 p[ pos?] % 10 == d[ pos ],替換后數列不變
現在的問題是,如何 O( n ) 求出每個區間的最小值呢?可以用笛卡爾樹,無腦跑出笛卡爾樹后,就可以dfs遍歷每個節點所能覆蓋的區間了
我寫的笛卡爾樹一般喜歡加個哨兵節點當做 root,所以在 dfs 的時候需要特判一下,防止死循環
代碼: ?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e6+100;const int mod=1000000007;const int Hash=10000019;struct Node
{int l,r;
}tree[N];int p[N],d[N],rk[N],root=N-1,num;stack<int>st;void build(int n)
{for(int i=0;i<n;i++){while(st.size()&&p[st.top()]>p[i])st.pop();tree[i].l=tree[st.top()].r;tree[st.top()].r=i;st.push(i);}
}void dfs(int k,int l,int r)
{if(k==root){if(tree[k].l)dfs(tree[k].l,l,r);elsedfs(tree[k].r,l,r);return;}if(l>r)return;if(l==r){rk[l]=num++;return;}if(p[k]==inf){for(int i=l;i<=r;i++)rk[i]=num++;return;}if(p[k]%10<d[k])dfs(tree[k].l,l,k),dfs(tree[k].r,k+1,r);elsedfs(tree[k].r,k+1,r),dfs(tree[k].l,l,k);
}void init(int n)
{num=0;for(int i=0;i<=n;i++)tree[i].l=tree[i].r=0;tree[root].l=tree[root].r=0;p[root]=-inf;while(st.size())st.pop();st.push(root);
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int w;cin>>w;while(w--){int n;scanf("%d",&n);init(n);LL pseed,pa,pb,pmod,dseed,da,db,dmod;scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&pseed,&pa,&pb,&pmod,&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[pseed%(i+1)],p[i]);pseed=(pseed*pa+pb)%pmod;}for(int i=0;i<n;i++)if(p[i]%10==d[i])//改變第i個位置沒有效果 p[i]=inf;build(n);dfs(root,0,n);LL ans=0,base=1;for(int i=0;i<=n;i++){ans=(ans+base*rk[i])%mod;base=base*Hash%mod;}printf("%lld\n",ans);}return 0;
}
?
總結
以上是生活随笔 為你收集整理的牛客多校3 - Sort the Strings Revision(笛卡尔树+分治) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。