BOJ 25081 Cocktail Party
sorohue가 PS하는 블로그
BOJ 25081 Cocktail Party
문제 링크입니다.
문제 요약
길이 $N$의 문자열이 주어집니다. 두 문자 $p$와 $q$에서 각각 시작하는 길이 $l$의 부분 문자열이 서로 같다면 두 문자를 $l$-닮음이라 합니다. 각 문자에는 정수 가중치 $w$가 할당되어 있습니다. $w$는 음수일 수 있습니다.
$0$ 이상 $N$ 미만의 모든 $l$에 대해, $l$-닮음인 두 문자의 최대 가중치 곱을 구하세요.
풀이
두 문자가 닮음이 되는 최대 $l$은 LCP 배열 상에서의 구간 최솟값으로 구할 수 있습니다. $l$이 감소함에 따라 함께 고를 수 있는 문자들의 집합이 합쳐지기만 하기 때문에, $l$이 클 때부터 역으로 처리하면서 Union-Find로 큰 $l$부터 합쳐주면 됩니다.
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
using pii = pair<int, int>;
struct Node{
int nxt[27];
int slink;
int len; int rpos;
Node(int a, int b, int c) : slink(a), len(b), rpos(c){for(int i = 0; i < 27; i++) nxt[i] = -1;}
};
struct SuffixAutomata{
vector<Node> v;
vector<array<int, 27>> e;
vector<int> SA, LCP;
vector<int> s;
int tot, last_depth = 0;
SuffixAutomata(){
v.push_back(Node(-1, 0, -1));
tot = 0;
}
void add(int c, int idx){
int now = tot;
v.push_back(Node(0, v[tot].len+1, idx));
tot = v.size()-1;
while(now >= 0 && v[now].nxt[c] == -1){
v[now].nxt[c] = tot;
now = v[now].slink;
}
if(now < 0) return;
int prv = v[now].nxt[c];
if(v[now].len+1 == v[prv].len){
v[tot].slink = prv; return;
}
int upd = v.size();
v.push_back(Node(v[prv].slink, v[now].len+1, idx));
v[prv].slink = upd; for(int i = 0; i < 27; i++) v[upd].nxt[i] = v[prv].nxt[i];
for(int j = now; j >= 0 && v[j].nxt[c] == prv; j = v[j].slink) v[j].nxt[c] = upd;
v[tot].slink = upd;
}
void get_tree(){
e.resize(v.size());
for(auto& i : e) i.fill(-1);
for(int i = 1; i < v.size(); i++) e[v[i].slink][s[v[i].rpos-v[v[i].slink].len]] = i;
}
void get_array(int now = 0, int depth = 0){
bool isLeaf = true;
for(int i = 0; i < 27; i++){
if(e[now][i] >= 0){
isLeaf = false;
get_array(e[now][i], v[e[now][i]].len);
if(last_depth > depth) last_depth = depth;
}
}
if(isLeaf){
SA.push_back(s.size()-depth+1);
LCP.push_back(min(last_depth, depth));
last_depth = depth;
}
}
void solve(string S){
reverse(S.begin(), S.end()); s.push_back(0);
for(auto& c : S) s.push_back(c-'a'+1);
for(int i = 0; i < s.size(); i++) add(s[i], i);
get_tree();
get_array();
}
} sfx;
array<int, 7> a[303030], na[303030];
ll ans, mx = -LONG_LONG_MAX;
int par[303030];
vector<int> e[303030];
int f(int x){
return par[x] < 0 ? x : par[x] = f(par[x]);
}
void u(int x, int y){
x = f(x); y = f(y);
ans -= (ll)par[x]*(par[x]-1)/2;
ans -= (ll)par[y]*(par[y]-1)/2;
par[x] += par[y]; par[y] = x;
ans += (ll)par[x]*(par[x]-1)/2;
if(a[x][1] > a[y][1]) a[x][2] = max(a[y][1], a[x][2]);
else a[x][2] = max(a[y][2], a[x][1]), a[x][1] = a[y][1];
a[x][3] = min(a[x][3], a[y][3]);
if(a[x][4] < a[y][4]) a[x][5] = min(a[y][4], a[x][5]);
else a[x][5] = min(a[y][5], a[x][4]), a[x][4] = a[y][4];
a[x][6] = max(a[x][6], a[y][6]);
if(a[x][2] >= 0) mx = max(mx, (ll)a[x][1]*a[x][2]);
if(a[x][5] <= 0) mx = max(mx, (ll)a[x][4]*a[x][5]);
mx = max(mx, (ll)a[x][3]*a[x][6]);
}
vector<pll> ret;
int main(){
cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
memset(par, -1, sizeof(par));
int n; string s; cin >> n >> s; sfx.solve(s);
for(int i = 1; i <= n; i++){
cin >> a[i][0];
a[i][1] = a[i][2] = -1; a[i][3] = 1234567890;
a[i][4] = a[i][5] = 1; a[i][6] = -1234567890;
if(a[i][0] >= 0) a[i][1] = a[i][3] = a[i][0];
if(a[i][0] <= 0) a[i][4] = a[i][6] = a[i][0];
}
for(int i = 1; i <= n; i++) na[i] = a[sfx.SA[i]]; swap(a, na);
for(int i = 2; i <= n; i++) e[sfx.LCP[i]].push_back(i);
for(int i = n-1; i >= 0; i--){
for(auto& t : e[i]) u(t-1, t);
if(!ans) ret.emplace_back(0, 0);
else ret.emplace_back(ans, mx);
}
for(int i = n-1; i >= 0; i--) cout << ret[i].first << ' ' << ret[i].second << '\n';
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.