NOI2015.品酒大会(后缀数组)
2016-01-08 14:06
134 查看
给出一个 长度为 n
的字符串,每一位有一个权值 val。定义两个位字符为 r 相似,是指分别从这两个字符开始,到后面的 r
个字符都相等。两个 r 相似的字符还有一个权值为这两个字符权值的乘积。问对于 r = 0, 1, 2, … , n - 1,统计出有多少种方法可以选出
2 个“r 相似”的字符,并回答选择 2 个”r
相似”的字符可以得到的权值的最大值。
首先说一个暴力的做法,可以得到
50 分:
先预处理出 1 - n 累加的结果。对于这个字符串求出
Height 数组,然后枚举 r ,按 r 把
Height 分组,然后统计方案数:假设组内有 x 个后缀,那么贡献的方案数就是 1 + 2 + ... + (x - 1),统计最大值:维护最大值和次大值,最小值和次小值,更新答案。
这种方法的复杂度主要看数据,如果数据很乱,导致 Height数组的最大值很小,那么这种方法就可以过,否则就会 TLE。
求 Height 数组,然后按从大到小的顺序排序,因为可以发现
Height 中的大值不会影响小值对答案的贡献。每次更新答案,将当前两个字符串合并,即用并查集维护一下,他们对于方案数的贡献就是这两个后缀所在的集合个数的乘积,同时维护一下最大最小值就行了。时间复杂度 O( nlogn )。
的字符串,每一位有一个权值 val。定义两个位字符为 r 相似,是指分别从这两个字符开始,到后面的 r
个字符都相等。两个 r 相似的字符还有一个权值为这两个字符权值的乘积。问对于 r = 0, 1, 2, … , n - 1,统计出有多少种方法可以选出
2 个“r 相似”的字符,并回答选择 2 个”r
相似”的字符可以得到的权值的最大值。
首先说一个暴力的做法,可以得到
50 分:
先预处理出 1 - n 累加的结果。对于这个字符串求出
Height 数组,然后枚举 r ,按 r 把
Height 分组,然后统计方案数:假设组内有 x 个后缀,那么贡献的方案数就是 1 + 2 + ... + (x - 1),统计最大值:维护最大值和次大值,最小值和次小值,更新答案。
这种方法的复杂度主要看数据,如果数据很乱,导致 Height数组的最大值很小,那么这种方法就可以过,否则就会 TLE。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 300005; const long long INF = 9223372036854775807; typedef long long LL; int n, a[MAX_N], sa[MAX_N], r[MAX_N], h[MAX_N], id1, id2; LL val[MAX_N], Mx, Mxx, Mn, Mnn; int ws[MAX_N], wv[MAX_N], wa[MAX_N], wb[MAX_N]; LL add[MAX_N], ans, ret; void da(int *a, int *sa, int n, int m) { int *x = wa, *y = wb; for (int i = 0; i < m; i ++) ws[i] = 0; for (int i = 0; i < n; i ++) ws[x[i] = a[i]] ++; for (int i = 1; i < m; i ++) ws[i] += ws[i - 1]; for (int i = n - 1; i >= 0; i --) sa[-- ws[x[i]]] = i; for (int k = 1; k <= n; k <<= 1) { int p = 0; for (int i = n - k; i < n; i ++) y[p ++] = i; for (int i = 0; i < n; i ++) if (sa[i] >= k) y[p ++] = sa[i] - k; for (int i = 0; i < n; i ++) wv[i] = x[y[i]]; for (int i = 0; i < m; i ++) ws[i] = 0; for (int i = 0; i < n; i ++) ws[wv[i]] ++; for (int i = 1; i < m; i ++) ws[i] += ws[i - 1]; for (int i = n - 1; i >= 0; i --) sa[-- ws[wv[i]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for (int i = 1; i < n; i ++) x[sa[i]] = (y[sa[i - 1]] == y[sa[i]]) && (y[sa[i - 1] + k] == y[sa[i] + k]) ? p - 1 : p ++; if (p >= n) break; m = p; } } void calc() { for (int i = 1; i <= n; i ++) r[sa[i]] = i; int k = 0, j; for (int i = 0; i < n; h[r[i ++]] = k) for (k ? k -- : 0, j = sa[r[i] - 1]; a[i + k] == a[j + k]; k ++); } void work(int x) { int sum = 0, j; for (int i = 2; i <= n; i = j + 1) { for (; h[i] < x && i <= n; i ++); for (j = i; h[j] >= x; j ++); if (i == j) continue; sum = 0; Mx = -INF; Mn = Mnn = INF; ans += add[j - i]; for (int k = i - 1; k < j; k ++) { if (val[sa[k]] > Mx) Mx = val[sa[k]], id1 = k; if (val[sa[k]] < Mn) Mn = val[sa[k]], id2 = k; } Mxx = -INF, Mnn = INF; for (int k = i - 1; k < j; k ++) { if (val[sa[k]] > Mxx && id1 != k) Mxx = val[sa[k]]; if (val[sa[k]] < Mnn && id2 != k) Mnn = val[sa[k]]; } ret = max(ret, max(Mxx * Mx, Mn * Mnn)); } } void init() { scanf("%d", &n); getchar(); for (int i = 0; i < n; i ++) { char c; scanf("%c", &c); a[i] = (int)c; } a = 0; Mx = Mxx = -INF; Mn = Mnn = INF; for (int i = 0; i < n; i ++) { scanf("%lld", &val[i]); if (Mx < val[i]) Mx = val[i], id1 = i; if (Mn > val[i]) Mn = val[i], id2 = i; } da(a, sa, n + 1, 128); calc(); for (int i = 1; i <= n; i ++) printf("%d ", sa[i]); printf("\n"); for (int i = 1; i <= n; i ++) printf("%d ", h[i]); printf("\n"); } void doit() { int mxh = -1; for (int i = 1; i <= n; i ++) mxh = max(mxh, h[i]); for (int i = 1; i <= n; i ++) add[i] = add[i - 1] + i; for (int i = 0; i < n; i ++) { if (Mxx < val[i] && i != id1) Mxx = val[i]; if (Mnn > val[i] && i != id2) Mnn = val[i]; } printf("%lld %lld\n", add[n - 1], max(Mx * Mxx, Mn * Mnn)); for (int i = 1; i < n; i ++) { if (i <= mxh) { ans = 0; ret = -INF; Mx = Mxx = -INF; Mn = Mnn = INF; work(i); printf("%lld %lld\n", ans, ret == -INF ? 0 : ret); } else printf("0 0\n"); } } int main() { init(); doit(); return 0; }满分算法:
求 Height 数组,然后按从大到小的顺序排序,因为可以发现
Height 中的大值不会影响小值对答案的贡献。每次更新答案,将当前两个字符串合并,即用并查集维护一下,他们对于方案数的贡献就是这两个后缀所在的集合个数的乘积,同时维护一下最大最小值就行了。时间复杂度 O( nlogn )。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 300005; typedef long long LL; int n, a[MAX_N], sa[MAX_N], r[MAX_N], h[MAX_N]; int ws[MAX_N], wv[MAX_N], wa[MAX_N], wb[MAX_N]; int f[MAX_N], sz[MAX_N]; LL ans[MAX_N], sum[MAX_N], mx[MAX_N], mn[MAX_N], val[MAX_N]; char c; struct node { int h, x, y; }g[MAX_N]; inline bool cmp(node a, node b) { return a.h > b.h; } int find(int x) { return x == f[x] ? x : (f[x] = find(f[x])); } void uniont(int x, int y) { f[y] = x; sz[x] += sz[y]; mx[x] = max(mx[x], mx[y]); mn[x] = min(mn[x], mn[y]); } void da(int *a, int *sa, int n, int m) { int *x = wa, *y = wb; for (int i = 0; i < m; i ++) ws[i] = 0; for (int i = 0; i < n; i ++) ws[x[i] = a[i]] ++; for (int i = 1; i < m; i ++) ws[i] += ws[i - 1]; for (int i = n - 1; i >= 0; i --) sa[-- ws[x[i]]] = i; for (int k = 1; k <= n; k <<= 1) { int p = 0; for (int i = n - k; i < n; i ++) y[p ++] = i; for (int i = 0; i < n; i ++) if (sa[i] >= k) y[p ++] = sa[i] - k; for (int i = 0; i < n; i ++) wv[i] = x[y[i]]; for (int i = 0; i < m; i ++) ws[i] = 0; for (int i = 0; i < n; i ++) ws[wv[i]] ++; for (int i = 1; i < m; i ++) ws[i] += ws[i - 1]; for (int i = n - 1; i >= 0; i --) sa[-- ws[wv[i]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for (int i = 1; i < n; i ++) x[sa[i]] = (y[sa[i - 1]] == y[sa[i]]) && (y[sa[i - 1] + k] == y[sa[i] + k]) ? p - 1 : p ++; if (p >= n) break; m = p; } } void calc() { for (int i = 1; i <= n; i ++) r[sa[i]] = i; int k = 0, j; for (int i = 0; i < n; h[r[i ++]] = k) for (k ? k -- : 0, j = sa[r[i] - 1]; a[i + k] == a[j + k]; k ++); } void init() { scanf("%d", &n); getchar(); for (int i = 0; i < n; i ++) { char c; scanf("%c", &c); a[i] = (int)c; } a = 0; for (int i = 1; i <= n; i ++) scanf("%lld", &val[i]); da(a, sa, n + 1, 128); calc(); for (int i = 1; i <= n; i ++) f[i] = i, mx[r[i - 1]] = val[i], mn[r[i - 1]] = val[i], sz[i] = 1; } void doit() { for (int i = 2; i <= n; i ++) g[i - 1].h = h[i], g[i - 1].x = i, g[i - 1].y = i - 1; sort(g + 1, g + n, cmp); memset(sum, 128, sizeof(sum)); for (int i = g[1].h, j = 1; i >= 0; i --) { ans[i] = ans[i + 1], sum[i] = sum[i + 1]; for (; j < n && g[j].h == i; j ++) { int x = find(g[j].x), y = find(g[j].y); sum[i] = max(sum[i], 1ll * mx[x] * mx[y]); sum[i] = max(sum[i], 1ll * mn[x] * mn[y]); ans[i] += 1ll * sz[x] * sz[y]; uniont(x,y); } } for (int i = 0; i < n; i ++) printf("%lld %lld\n", ans[i], ans[i] == 0 ? 0 : sum[i]); } int main() { init(); doit(); return 0; }
相关文章推荐
- IDEA中GITHUB配置
- Hibernate向MySQL插入中文数据--乱码解决
- 互联网打赏
- Java-代碼混淆编译器
- lwIP(V1.3.0)RAW_API译文
- 搭建Hadoop源代码阅读环境
- 从头认识java-17.2 基本的线程机制(1)-初识多线程-2
- 自己发现写博客是很好整理自己知识点的好方法
- oc之可变字典创建 添加 删除 遍历
- linux中字符串转换函数 simple_strtoul
- 187,使用手势控制图片的缩放
- 从零开始nodejs系列文章-nodejs到底能干什么
- Java EE V7.0学习笔记-JBoss Tools 4.2.3.Final配套的WildFly版本为8.x
- 理解Java虚拟机体系结构
- 一步一步安装hadoop1.2.1
- 访问win7的d$这种默认共享时拒绝访问
- VS集成Qt环境搭建
- android、java制作sdk以及自动生成文档
- 《Java编程思想》第四版笔记
- js 中实现aop