博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
luogu解题报告:P2178[NOI2015]品酒大会
阅读量:4965 次
发布时间:2019-06-12

本文共 3653 字,大约阅读时间需要 12 分钟。

题意

给定一个字符串S和一个权值函数a(i),求对于0r<n,长度为r的相等子串对个数和相等子串对开头两位置权值乘积的最大值。

My Idea

首先看到标签知道是后缀数组,那么就先搞出height数组。

考虑一个naive的方法:从大到小枚举r,每次只考虑两位置最大为r匹配(而不考虑5匹配也能是3匹配的)。我们建一棵height的线段树。对于每一次,我们先找出height中的每一个height[p] = r的位置,然后向前后二分并用线段树判断,找到最大的使height[p]为最小值的区间,然后左右乘一乘就算出来了。

然而复杂度是O(nlg2n)的,300000的时候显然会超时(当然松爷肯定能卡过去)。

正解

居然是并查集……

最后一步变成边计算边合并,维护一个带权并查集就好了。神思路……
如果用DA就是O(nlgn+nα(n)),如果用DC3就是O(nα(n))了(不过蒟蒻显然不会)..

Code

#include 
using namespace std;const int maxn = 300005;struct S_A { int A[maxn], SA[maxn], C[maxn], rank[maxn], h[maxn], n; struct p { int x[2], id; p(){} p(int a, int b, int c):id(c) {x[0] = a, x[1] = b;} } RE[maxn], RT[maxn]; S_A():n(0){} void radix_sort() { for (int y = 1; y >= 0; y--) { memset(C, 0, sizeof C); register int i; for (i = 1; i <= n; i++) C[RE[i].x[y]]++; for (i = 1; i < maxn; i++) C[i] += C[i-1]; for (i = n; i >= 1; i--) RT[C[RE[i].x[y]]--] = RE[i]; for (i = 1; i <= n; i++) RE[i] = RT[i]; } for (int i = 1; i <= n; i++) { rank[RE[i].id] = rank[RE[i-1].id]; if (RE[i].x[1] != RE[i-1].x[1] || RE[i].x[0] != RE[i-1].x[0]) rank[RE[i].id]++; } } void build_sa() { for (int i = 1; i <= n; i++) RE[i] = p(A[i], 0, i); radix_sort(); for (int k = 1; k < n; k <<= 1) { for (int i = 1; i <= n; i++) RE[i] = p(rank[i], i+k<=n?rank[i+k]:0, i); radix_sort(); } for (int i = 1; i <= n; i++) SA[rank[i]] = i; } void build_height() { for (int ht = 0, i = 1; i <= n; i++) { if (rank[i] == 1) ht = 0; else { int k = SA[rank[i]-1]; if (--ht < 0) ht = 0; while (A[i+ht] == A[k+ht]) ht++; } h[rank[i]] = ht; } } void init(const char *str) { for (; *str != '\0'; ++str) A[++n] = *str-'a'+1; build_sa(); build_height(); }}SA;char str[maxn];long long a[maxn];long long ansx[maxn], ansy[maxn];struct p { int to, next;} edge[maxn];int head[maxn], top = 0;void push(int i, int j) { edge[++top].to = j; edge[top].next = head[i]; head[i] = top;}int fa[maxn];long long siz[maxn], mx[maxn], mn[maxn];long long cnt = 0, maxd = LONG_LONG_MIN;int findp(int i){ return fa[i]?fa[i]=findp(fa[i]):i; }void link(int i, int j) { //cout << i << " " << j << endl; if (!i || !j) return; int p = findp(i), q = findp(j); if (p != q) { maxd = max(maxd, max(mn[p]*mn[q], max(mx[p]*mx[q], max(mn[p]*mx[q], mx[p]*mn[q])))); cnt += siz[p]*siz[q]; fa[p] = q; siz[q] += siz[p]; mx[q] = max(mx[q], mx[p]); mn[q] = min(mn[q], mn[p]); }}int main() { int n; scanf("%d", &n); scanf("%s", str); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), siz[i] = 1, fa[i] = 0; SA.init(str); for (int i = 1; i <= n; i++) mn[i] = mx[i] = a[SA.SA[i]]; for (int i = 1; i <= n; i++) push(SA.h[i], i); for (int i = n-1; i >= 0; i--) { for (int j = head[i]; j; j = edge[j].next) { int to = edge[j].to, p = findp(to), q = findp(to-1); maxd = max(maxd, max(mn[p]*mn[q], max(mx[p]*mx[q], max(mn[p]*mx[q], mx[p]*mn[q])))); } for (int j = head[i]; j; j = edge[j].next) { int to = edge[j].to; link(to-1, to); } ansx[i] = cnt; ansy[i] = maxd; if (ansy[i] == LONG_LONG_MIN) ansy[i] = 0; } for (int i = 0; i < n; i++) printf("%lld %lld\n", ansx[i], ansy[i]); return 0;}

转载于:https://www.cnblogs.com/ljt12138/p/6684343.html

你可能感兴趣的文章
【还是畅通工程 HDU - 1233】【Kruskal模板题】
查看>>
【hdu 2544最短路】【Dijkstra算法模板题】
查看>>
【Calling Circles UVA - 247 】【Floyd + dfs】
查看>>
【改革春风吹满地 HDU - 2036 】【计算几何-----利用叉积计算多边形的面积】
查看>>
【Audiophobia UVA - 10048 】【Floyd算法】
查看>>
【Fishing Master HDU - 6709 】【贪心】
查看>>
【Bazinga HDU - 5510 】【考察strstr()的使用】【贪心】
查看>>
【Windows Of CCPC HDU - 6708】【打表,找规律】
查看>>
【Bit String Reordering UVALive - 6832 】【模拟】
查看>>
(转载)博弈汇总【巴什博奕,威佐夫博弈,尼姆博弈,斐波那契博弈】
查看>>
【数据结构作业】-【带头结点的单链表就地逆置】
查看>>
【Miscalculation UVALive - 6833 】【模拟】
查看>>
【Pet HDU - 4707 】【利用并查集找深度】
查看>>
《Java程序设计实验》 软件工程18-1,3 OO实验2
查看>>
【Herding HDU - 4709 】【数学(利用叉乘计算三角形面积)】
查看>>
【Difference Between Primes HDU - 4715】【素数筛法打表+模拟】
查看>>
【(图) 旅游规划 (25 分)】【Dijkstra算法】
查看>>
【表达式转换 (25 分)】
查看>>
【7-9 有重复的数据I (20 分)】【此题卡输入,需要自己写个输入挂】
查看>>
JRebel安装部署,激活
查看>>