題鏈:
題解:
網上大多數的方法都是用並查集維護。
這裡呢,給出另一種自己yy的解法(但實際上本質差不多吧):
字尾陣列,rmq,單調棧
1).預處理
1].首先對字串字尾排序,得到 sa[i],rank[i],height[i]
2].然後維護出 l[i]:表示在字尾陣列中,排名最小(記其排名為 l[i])的字尾與排名為 i的字尾的lcp>=hei[i]
同理 r[i]:表示在字尾陣列中,排名最大(記其排名為 r[i])的字尾與排名為 i的字尾的lcp>hei[i](注意這裡沒有'=',避免重複)
這兩個陣列可以用單調棧 o(n)維護出來。
3].把輸入的權值陣列(a陣列)按照排好序的字尾的順序重新排序,得到 b陣列:b[i]=a[sa[i]];
話句話說吧:b[i]表示 排名第i的字尾的首字母對應的權值
然後對 b數組建兩個 st表,乙個用於詢問區間最大值,乙個用於詢問區間最小值。
2).貢獻答案
記 num[i]表示相似值為 i的方案數(詢問一),val[i]表示相似值為 i的最大美味度(詢問二)。
不難發現,l[i],r[i]陣列可以理解為 在字尾陣列的 [l[i],r[i]]這個區間中,hei[i]為最小值。
那麼這意味著,區間[l[i],i-1]中的任意乙個字尾與區間[i,r[i]]中的任意乙個字尾的 lcp==hei[i]
那麼便可以貢獻答案了:num[hei[i]]+=(i-l[i])*(r[i]-i+1)
同時查詢 st表,得到區間[l[i],i-1]的最大最小權值 max1,min1,以及區間[i,r[i]]最大最小權值 max2,min2。
那麼:val[hei[i]]=max(val[hei[i]],max(max1*max2,min1*min2))
(因為有負數,所以要考慮最小值的相乘)
注意到以上貢獻的值都是建立在 lcp(最長公共字首)上的,
但還有 lcp-1,lcp-2,lcp-3......這些沒有被貢獻到,
所以就要最後反向遍歷一下兩個答案陣列,用後面的去更新前面的:
num[i]+=num[i+1]; val[i]=max(val[i],val[i+1]);
然後就沒了。(第一道獨立且一遍 ac的 noi題目,雖然題有點水,但還是偷樂一下咯。)
除開倍增演算法和求st表的複雜度 o(n)
**:
#include#include#include#define maxn 300500#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
char s[maxn];
int a[maxn],b[maxn];
int sa[maxn],rak[maxn],hei[maxn],log2[maxn],l[maxn],r[maxn];
struct st
for(int i=0;i=hei[i]) top--;
l[i]=stp[top]; top++;
stk[top]=hei[i]; stp[top]=i;
} top=n; stp[top]=n;
for(int i=n-1;i>=0;i--)
/*for(int i=0;i=0;i--)
for(int i=0;i>1]+1;
scanf("%d",&n); scanf("%s",s);
for(int i=0;i
UOJ 131 NOI 2015 品酒大會
求出字尾陣列和height陣列後,從大到小掃相似度進行合併,每次相當於合併兩個緊挨著的區間。合併區間可以用並查集來實現,每個區間的資訊都記錄在這個區間的並查集的根上,合併並查集時用乙個根的資訊更新另乙個根的資訊同時計算兩個答案。時間複雜度 o n log n include include incl...
uoj131 NOI2015 品酒大會
題目鏈結 很容易想到p和q r相似 就等價於在字尾陣列中q與p之間的height值 ge r 也就是說 的那些height值會把排好序後的字尾分割成若干段,可以想到倒序列舉r,每當r減小1時,中間把他們分開的那些 屏障 就會減少一些,兩邊的集合會合併到一起,現在的問題就是給定乙個集合,支援合併操作,...
UOJ131 NOI2015 品酒大會
考前掙扎 bu shi 之前留下來的坑 首先注意到sam的parent樹 是反串的字尾樹 也就是原串的字首樹 這個性質很重要 所以說我們在樹上統計的時候兩個點的lca就是兩個字尾串的lcp 於是可以替代字尾陣列 嘿嘿嘿 然後嘞我們樹形dp 統計的size就是以這個串為字首的子串個數 然後我們通過差分...