洛谷P2178 品酒大會

題意:若兩個字符開始的後面r個字符都同樣,則稱這兩個字符是r類似的。它們也是r-1類似的。數組

對於r∈[0,n)分別求有多少種方案,其中權值最大方案權值是多少。此處權值是選出的兩個字符的權值之積。ide

解:後綴自動機吊打後綴數組!!!spa

先看第一問,咱們考慮後綴自動機上每一個節點的貢獻。顯然cnt>1的節點纔會有貢獻。code

它會對r ∈ len[fail[x]] + 1  ~  len[x]這一段的答案產生C(cntx,2)的貢獻。這就是一個區間加法。blog

有個小問題,若是r減小那麼相應的可選的其實會變多,可是此處咱們不統計,那些會在以另外一個字符結尾的別的節點上考慮到。string

這樣第一問就解決了。第二問?發現問題很大...一個節點的每一個串都是結尾相同,開頭不一樣。那麼開頭的權值之積就很差維護。it

由於結尾相同,因此考慮反着建後綴自動機,而後就變成了開頭相同了。那麼如何維護乘積最大值呢?io

考慮到一個節點的末尾所在位置,也就是它的right集合。顯然就是fail樹的子樹中全部在主鏈上的節點。event

因而每一個點維護子樹最大值便可。由於有負數因此還要最小值。ast

這樣一個節點對第二問的貢獻就是區間取max了。這兩問的操做均可以用線段樹搞定。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 typedef long long LL;
  6 const int N = 600010;
  7 
  8 int tr[N][26], tot = 1, fail[N], len[N], cnt[N], bin[N], topo[N], last = 1, val[N], n;
  9 int max1[N], max2[N], min1[N], min2[N];
 10 char str[N];
 11 LL tag[N << 1], large[N << 1];
 12 
 13 inline void insert(char c) {
 14     int f = c - 'a';
 15     int p = last, np = ++tot;
 16     last = np;
 17     len[np] = len[p] + 1;
 18     cnt[np] = 1;
 19     while(p && !tr[p][f]) {
 20         tr[p][f] = np;
 21         p = fail[p];
 22     }
 23     if(!p) {
 24         fail[np] = 1;
 25     }
 26     else {
 27         int Q = tr[p][f];
 28         if(len[Q] == len[p] + 1) {
 29             fail[np] = Q;
 30         }
 31         else {
 32             int nQ = ++tot;
 33             len[nQ] = len[p] + 1;
 34             fail[nQ] = fail[Q];
 35             fail[Q] = fail[np] = nQ;
 36             memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 37             while(tr[p][f] == Q) {
 38                 tr[p][f] = nQ;
 39                 p = fail[p];
 40             }
 41         }
 42     }
 43     return;
 44 }
 45 
 46 inline void update(int x, int y) {
 47     int t[4];
 48     t[0] = max1[x];
 49     t[1] = max2[x];
 50     t[2] = max1[y];
 51     t[3] = max2[y];
 52     std::sort(t, t + 4);
 53     max1[x] = t[3];
 54     max2[x] = t[2];
 55     t[0] = min1[x];
 56     t[1] = min2[x];
 57     t[2] = min1[y];
 58     t[3] = min2[y];
 59     std::sort(t, t + 4);
 60     min1[x] = t[0];
 61     min2[x] = t[1];
 62     return;
 63 }
 64 
 65 inline void prework() {
 66     for(int i = 1; i <= tot; i++) {
 67         bin[len[i]]++;
 68     }
 69     for(int i = 1; i <= tot; i++) {
 70         bin[i] += bin[i - 1];
 71     }
 72     for(int i = 1; i <= tot; i++) {
 73         topo[bin[len[i]]--] = i;
 74     }
 75     for(int i = tot; i >= 1; i--) {
 76         int a = topo[i];
 77         cnt[fail[a]] += cnt[a];
 78         update(fail[a], a);
 79     }
 80     return;
 81 }
 82 
 83 inline void pushdown(int o) {
 84     if(tag[o]) {
 85         tag[o << 1] += tag[o];
 86         tag[o << 1 | 1] += tag[o];
 87         tag[o] = 0;
 88     }
 89     large[o << 1] = std::max(large[o << 1], large[o]);
 90     large[o << 1 | 1] = std::max(large[o << 1 | 1], large[o]);
 91     return;
 92 }
 93 
 94 void add(int L, int R, LL v, int l, int r, int o) {
 95     if(L <= l && r <= R) {
 96         tag[o] += v;
 97         return;
 98     }
 99     int mid = (l + r) >> 1;
100     pushdown(o);
101     if(L <= mid) {
102         add(L, R, v, l, mid, o << 1);
103     }
104     if(mid < R) {
105         add(L, R, v, mid + 1, r, o << 1 | 1);
106     }
107     return;
108 }
109 
110 void out(int l, int r, int o) {
111     if(l == r) {
112         if(r != n) {
113             printf("%lld %lld \n", tag[o], tag[o] ? large[o] : 0);
114         }
115         return;
116     }
117     int mid = (l + r) >> 1;
118     pushdown(o);
119     out(l, mid, o << 1);
120     out(mid + 1, r, o << 1 | 1);
121     return;
122 }
123 
124 void change(int L, int R, LL v, int l, int r, int o) {
125     if(v <= large[o]) {
126         return;
127     }
128     if(L <= l && r <= R) {
129         large[o] = v;
130         return;
131     }
132     int mid = (l + r) >> 1;
133     pushdown(o);
134     if(L <= mid) {
135         change(L, R, v, l, mid, o << 1);
136     }
137     if(mid < R) {
138         change(L, R, v, mid + 1, r, o << 1 | 1);
139     }
140     return;
141 }
142 
143 int main() {
144     memset(max1, ~0x3f, sizeof(max1));
145     memset(max2, ~0x3f, sizeof(max2));
146     memset(min1, 0x3f, sizeof(min1));
147     memset(min2, 0x3f, sizeof(min2));
148     memset(large, ~0x3f, sizeof(large));
149     scanf("%d", &n);
150     scanf("%s", str + 1);
151     int l1 = -0x3f3f3f3f, l2 = -0x3f3f3f3f, s1 = 0x3f3f3f3f, s2 = 0x3f3f3f3f;
152     for(int i = 1; i <= n; i++) {
153         scanf("%d", &val[i]);
154         if(l1 < val[i]) {
155             l2 = l1;
156             l1 = val[i];
157         }
158         else if(l2 < val[i]) {
159             l2 = val[i];
160         }
161         if(s1 > val[i]) {
162             s2 = s1;
163             s1 = val[i];
164         }
165         else if(s2 > val[i]) {
166             s2 = val[i];
167         }
168     }
169     for(int i = n; i >= 1; i--) {
170         insert(str[i]);
171         max1[last] = min1[last] = val[i];
172     }
173     prework();
174     //
175     for(int i = 2; i <= tot; i++) {
176         if(cnt[i] < 2) {
177             continue;
178         }
179         // len[fail[i]] + 1  ~  len[i]
180         add(len[fail[i]] + 1, len[i], 1ll * cnt[i] * (cnt[i] - 1) / 2, 1, n, 1);
181         change(len[fail[i]] + 1, len[i], std::max(1ll * max1[i] * max2[i], 1ll * min1[i] * min2[i]), 1, n, 1);
182     }
183 
184     printf("%lld %lld \n", 1ll * n * (n - 1) / 2, std::max(1ll * l1 * l2, 1ll * s1 * s2));
185     out(1, n, 1);
186     return 0;
187 }
AC代碼
相關文章
相關標籤/搜索