#include
#include
#include
using namespace std;
const int maxn = 1000010;
int N, M;
char s[maxn];
//x是第一关键字,y是第二关键字,c是出现的次数
int sa[maxn], x[maxn], y[maxn], c[maxn], rk[maxn], height[maxn];
void get_sa() {
//x[i]是第i个元素的第一关键字
for (int i = 1; i <= N; i++) c[x[i]] = s[i]]++;
for (int i = 2; i <= M; i++) c[i] += c[i - 1];
for (int i = N; i >= 1; i--) sa[c[x[i]]--] = i;
for (int k = 1; k <= N; k <<= 1) {
int num = 0;
//y[i]表示第二关键字排名为i的数,第一关键字的位置
//第n-k+1到第n位是没有第二关键字的 所以排名在最前面
for (int i = N - k + 1; i <= N; i++) y[++num] = i;
//排名为i的数 在数组中是否在第k位以后
//如果满足(sa[i]>k) 那么它可以作为别人的第二关键字,就把它的第一关键字的位置添加进y就行了
for (int i = 1; i <= N; i++) {
if (sa[i] > k) {
y[++num] = sa[i] - k;
}
}
for (int i = 1; i <= M; i++) c[i] = 0;
for (int i = 1; i <= N; i++) c[x[i]]++;
for (int i = 2; i <= M; i++) c[i] += c[i - 1];
for (int i = N; i; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x, y);
x[sa[1]] = 1, num = 1;
for (int i = 2; i <= N; i++) {
x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
}
if (num == N) break;
M = num;
}
}
void get_height() {
for (int i = 1; i <= N; i++) rk[sa[i]] = i;
for (int i = 1, k = 0; i <= N; i++) {
if (rk[i] == 1) continue;
if (k) k--;
int j = sa[rk[i] - 1];
while (i + k <= N && j + k <= N && s[i + k] == s[j + k]) k++;
height[rk[i]] = k;
}
}
int main() {
scanf("%s", s + 1);
N = strlen(s + 1), M = 122;
get_sa();
get_height();
for (int i = 1; i <= N; i++) printf("%d ", sa[i]);
puts("");
for (int i = 1; i <= N; i++) printf("%d ", height[i]);
puts("");
return 0;
}
char str[maxn];
//rk[i] 表示下标为i开头后缀的排名
//sa[i] 表示第i小的后缀首字母对应原字符串下标。
int rk[maxn], sa[maxn], tmp[maxn];
int N, k;
bool cmp(int i, int j)
{
if(rk[i] != rk[j]) return rk[i] < rk[j];
else{
int ri = i + k <= N ? rk[i + k] : -1;
int rj = j + k <= N ? rk[j + k] : -1;
return ri < rj;
}
}
//if s is a string, please use: get_sa(char s[], int sa[])
void get_sa(int S[], int sa[])
{
for(int i = 0; i <= N; i++){
sa[i] = i;
rk[i] = i < N ? S[i] : -1;
}
for(k = 1; k <= N; k <<= 1){
sort(sa, sa + N + 1, cmp);
tmp[sa[0]] = 0;
for(int i = 1; i <= N; i++){
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
}
for(int i = 0; i <= N; i++){
rk[i] = tmp[i];
}
}
}
#include
#include
#include
using namespace std;
//节点数量开成两倍
const int maxn = 2000010;
int tot = 1, last = 1;
struct Node {
int len, fa;
int ch[26];
}node[maxn];
char str[maxn];
typedef long long ll;
ll f[maxn], ans;
void extend(int c) {
//求蓝边
int p = last, np = last = ++tot;
f[tot] = 1;
node[np].len = node[p].len + 1;
for (; p && !node[p].ch[c]; p = node[p].fa) node[p].ch[c] = np;
if (!p) node[np].fa = 1;
else {
int q = node[p].ch[c];
if (node[q].len == node[p].len + 1) node[np].fa = q;
else {
int nq = ++tot;
node[nq] = node[q], node[nq].len = node[p].len + 1;
node[q].fa = node[np].fa = nq;
for (; p && node[p].ch[c] == q; p = node[p].fa) node[p].ch[c] = nq;
}
}
}
int h[maxn], e[maxn], ne[maxn], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
//求绿边
for (int i = h[u]; i != -1; i = ne[i]) {
dfs(e[i]);
f[u] += f[e[i]];
}
if (f[u] > 1) ans = max(ans, f[u] * node[u].len);
}
int main() {
scanf("%s", str);
for (int i = 0; str[i]; i++) extend(str[i] - 'a');
memset(h, -1, sizeof h);
for(int i = 2; i <= tot; i++) add(node[i].fa, i);
dfs(1);
printf("%lld\n", ans);
return 0;
}
给定一个有 N N N 个点(编号 0 , 1 , . . . , N − 1 0,1,...,N-1 0,1,...,N−1)的树,每条边都有一个权值(不超过 1000 1000 1000)。树上两个节点 x x x 与 y y y 之间的路径长度就是路径上各条边的权值之和。求长度不超过 K K K 的路径有多少条。
三种情况:路径两端点在不同的子树中;路径的一个端点是重心;路径的两个端点都在重心之中。复杂度 O ( n log 2 n ) O(n \log^2 n) O(nlog2n)
#include
using namespace std;
const int N = 10010, M = N * 2;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
inline void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
bool st[N];
int p[N], q[N];
int get_size(int u, int fa)
{
if(st[u]) return 0;
int res = 1;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
res += get_size(v, u);
}
return res;
}
int get_wc(int u, int fa, int tot, int& wc)
{
if(st[u]) return 0;
int sz = 1, ms = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
int t = get_wc(v, u, tot, wc);
ms = max(ms, t);
sz += t;
}
ms = max(ms, tot - sz);
if(ms <= tot / 2) wc = u;
return sz;
}
void get_dist(int u, int fa, int dist, int& qt)
{
//printf("###\n");
if(st[u]) return;
q[qt++] = dist;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
get_dist(v, u, dist + w[i], qt);
}
}
int get(int a[], int k)
{
sort(a, a + k);
int res = 0;
for(int i = 0, j = k - 1; i < k && i <= j; i++)
{
while(i < j && a[i] + a[j] > m) j--;
res += j - i;
}
return res;
}
int calc(int u)
{
// printf("***\n");
if(st[u]) return 0;
get_wc(u, -1, get_size(u, -1), u);
st[u] = true;
int res = 0, pt = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int qt = 0, v = e[i];
get_dist(v, -1, w[i], qt);
res -= get(q, qt);
for(int k = 0; k < qt; k++)
{
if(q[k] <= m) res++;
p[pt++] = q[k];
}
}
res += get(p, pt);
for(int i = h[u]; i != -1; i = ne[i]) res += calc(e[i]);
return res;
}
int main()
{
while(cin >> n >> m, n)
{
memset(h, -1, sizeof h);
memset(st, 0, sizeof st);
idx = 0;
for(int i = 1; i < n; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
printf("%d\n", calc(0));
}
return 0;
}
给定一棵 N N N 个节点的树,每条边带有一个权值。求一条简单路径,路径上各条边的权值和等于 K K K,且路径包含的边的数量最少。
也是考虑这三种情况:路径两端点在不同的子树中;路径的一个端点是重心;路径的两个端点都在重心之中。
#include
#define x first
#define y second
using namespace std;
const int N = 200010, M = N * 2, S = 1000010, INF = 0x3f3f3f3f;
typedef pair<int, int> P;
int h[N], e[M], ne[M], w[M], idx;
int n, m;
P p[N], q[N];
int f[S];
bool st[N];
int ans = INF;
inline void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int get_size(int u, int fa)
{
if(st[u]) return 0;
int sz = 1;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
sz += get_size(v, u);
}
return sz;
}
int get_wc(int u, int fa, int tot, int& wc)
{
if(st[u]) return 0;
int sz = 1, ms = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
int t = get_wc(v, u, tot, wc);
sz += t;
ms = max(ms, t);
}
ms = max(ms, tot - sz);
if(ms <= tot / 2) wc = u;
return sz;
}
void get_dist(int u, int fa, int dist, int cnt, int& qt)
{
if(st[u] || dist > m) return;
q[qt++] = {dist, cnt};
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
get_dist(v, u, dist + w[i], cnt + 1, qt);
}
}
void calc(int u)
{
if(st[u]) return;
get_wc(u, -1, get_size(u, -1), u);
st[u] = true;
int pt = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i], qt = 0;
get_dist(v, u, w[i], 1, qt);
for(int k = 0; k < qt; k++)
{
auto& t = q[k];
if(t.x == m) ans = min(ans, t.y);
ans = min(ans, f[m - t.x] + t.y);
p[pt++] = t;
}
for(int k = 0; k < qt; k++)
{
auto& t = q[k];
f[t.x] = min(f[t.x], t.y);
}
}
for(int k = 0; k < pt; k++)
{
auto& t = p[k];
f[t.x] = INF;
}
for(int i = h[u]; i != -1; i = ne[i]) calc(e[i]);
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(f, 0x3f, sizeof f);
for(int i = 1; i < n; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
calc(0);
if(ans == INF) ans = -1;
printf("%d\n", ans);
return 0;
}
动态点分治用来解决 带点权/边权修改 的树上路径信息统计问题。