H. Sort the Strings Revision (笛卡爾樹)
阿新 • • 發佈:2020-07-24
-
題意:
一個s串滿足s[i] = i % 10,給出p,d陣列的構造方法,每次將s串p[i]位換成d[i], 總共得到n + 1個串,對它們進行排名。第i次操作後的排名為r[i];
輸出 \((\sum_{i=0}^{n}(r_{i}*10000019^{i}))\%(1e9+7)\) -
題解:
-
很關鍵的一點是p[]是一個排列,所以對s串每一位只修改一次。我們從低位到高位去修改,如果修改p[i]位要變成的數小於該位原來的數(p[i]%10>d[i]),那麼修改p[i]之後位得到的的串字典序一定小於修改p[i]前得到的字串。所以可以用這一次修改將字串排名分成兩類,一類佔字典序大的部分,一類佔字典序小的部分。再繼續分治對每個部分用其P[i]最小的位進行劃分。一直劃分下去就能得到所有的排名。
-
關鍵是當劃分成兩部分後怎麼快速確定這個處理區間的最小值位置,RMQ問題。這裡可以用O(n)的笛卡爾樹來處理。笛卡爾樹根節點是整個區間最小值的位置。左子節點是最小位置左邊區間的最小值的位置,右子節點是最小值右邊區間最小值所在位置。
-
分治時,如果 \(p[i]\%10>d[i]\) , 其中i是當前區間最小值所在位置,那麼其左邊的區間操作形成的字串rk+右邊個數(佔rk高位)。右邊不影響(佔rk低位)
dfs(ls[index], l, index, rank + (p[index] % 10 > d[index]) * (r - index));
-
如果 \(p[i]\%10<d[i]\)
dfs(rs[index], index + 1, r, rank + (p[index] % 10 < d[index]) * (index - l + 1));
-
如果 \(p[i]\%10=d[i]\) 時,這一位操作不會影響任何其他位的rk, 一個技巧,讓p[i]=INF, 這樣這一位就在迪卡爾樹的葉子節點上,最後處理時直接確定rk就行。同時如果處理到邊界,及葉子節點,說明這個操作處理這個區間的所能處理的最高位,能影響它的低位都處理了,根據前面的rk累計可以直接確定其rk。
if(p[index] == INF || l >= r) { for(int i = l; i <= r; ++ i) rk[i] = rank + (i - l); return; }
-
-
程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6+10; const ll mod = 1e9 + 7; const ll INF = 1 << 30; // int T, n; ll p[N], d[N]; int rk[N]; int ls[N], rs[N]; int st[N], top; void Build() { st[0]=0; for(int i=0;i<n;i++){ int k=st[0]; while(k>0&&p[st[k]]>p[i]) k--; if(k) rs[st[k]]=i; if(k<st[0]) ls[i]=st[k+1]; st[++k]=i; st[0]=k; } } void dfs(int index, int l, int r, int rank){ if(p[index] == INF || l >= r) { for(int i = l; i <= r; ++ i) rk[i] = rank + (i - l); return; } dfs(ls[index], l, index, rank + (p[index] % 10 > d[index]) * (r - index)); dfs(rs[index], index + 1, r, rank + (p[index] % 10 < d[index]) * (index - l + 1)); } int main() { scanf("%d",&T); while(T --){ scanf("%d",&n); ll pseed, pa, pb, pmod; ll dseed, da, db, dmod; 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; for(int i = 1; i < n; ++ i){ swap(p[pseed % (i + 1)], p[i]); pseed = (1ll * pseed * pa % pmod + 1ll * pb) % pmod; } for(int i = 0; i < n; ++ i){ d[i] = dseed % 10; dseed = (1ll * dseed * da % dmod + 1ll * db) % dmod; if(p[i] % 10 == d[i]) p[i] = INF; } Build(); for(int i = 0; i <= n; ++ i) rk[i] = 0; dfs(st[1], 0, n, 0); ll res = 0, temp = 1; for(int i = 0; i <= n; ++ i){ res = (res + 1ll * rk[i] * temp % mod) % mod; temp = temp * 10000019ll % mod; } printf("%lld\n",res); } return 0; }