b2c交易模式的网站有哪些/百度关键词查询工具
给定一个长为n(3e5)的字符串,如果两个后缀l,rl,rl,r的lcplcplcp至少为iii,那么称它们是i相似i相似i相似的。每个位置还有一个权值a(±1e9)。
现在对于每个i=0到n−1i=0到n-1i=0到n−1,问有多少个i相似i相似i相似的无序对(l,r)(l,r)(l,r),并且输出最大的al∗ara_l*a_ral∗ar
和lcp有关,自然想到后缀数组。跑一遍后缀数组求出height之后,这题就和字符串没什么关系了。。
考虑从大到小找答案,按height从大到小遍历id,每一个id的意义是id和id-1这两种酒是height[id]相似的。
也就是说,每个id的意义相当于把id-1和id这两种酒连起来了。
和并查集非常像,但每次合并之后需要知道新增了多少个无序对以及合并后新集合无序对的最大积。
易知新增无序对数等于两个集合的size之积。
为了求最大积,我们可以维护每个集合的最大值、最小值、最大积,然后对【原来的最大积】、【最大值之积】、【最小值之积】取max,就是新的最大积。
总结:
- 好多题的字符串只是一个壳。。。内部全是其它东西。
- 并查集的最大积可以通过维护最大值,最小值,历史最大积维护。
- 洛谷黑题第二道,独立做出的哟
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 300016, MOD = 1000000007;char str[M];
namespace SA
{ /* 后缀数组 */int sa[M], ra[M], height[M]; //后缀三数组,sa和ra下标从0开始,height下标从1开始int t1[M], t2[M], c[M]; // 用于基数排序的三个辅助数组void build(char *str, int n, int m) // 构造后缀三数组,字符串下标从0开始,n表示长度,m表示字符集大小{ str[n] = 0;n++; int i, j, p, *x = t1, *y = t2; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[i]=str[i]]++; for(i = 1; i < m; i++) c[i] += c[i-1]; for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i; for(j = 1; j <= n; j<<=1) { p = 0; for(i = n-j; i < n; i++) y[p++] = i; for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i]-j; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[y[i]]]++; for(i = 1; i < m; i++) c[i] += c[i-1]; for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i++) x[sa[i]] = (y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]) ? p-1 : p++; if(p >= n) break; m = p; } n--; for(int i = 0; i <= n; i++) ra[sa[i]] = i; for(int i=0, j=0, k=0; i < n; i++) { if(k) k--; j = sa[ra[i]-1]; while(str[i+k]==str[j+k]) k++; height[ra[i]] = k; } }
};struct UFS
{int fa[M], cnt[M]; //父亲(当父亲不是自己时失效),元素个数ll mx[M], mi[M], mul_mx[M];//集合中元素个数,最大值,最小值,最大对乘积void init(int n, ll *pri){for(int i=1; i<=n; ++i){cnt[i] = 1; fa[i] = i;mx[i] = mi[i] = pri[i];mul_mx[i] = LLONG_MIN;}}int find(int i) {return fa[i]==i ? i : fa[i] = find(fa[i]);}//合并集合,返回新增对数,最大对乘积pair<ll,ll> join(int a, int b) {a = find(a), b = find(b);ll res_cnt = 1ll*cnt[a]*cnt[b];ll res_max = max(mul_mx[a], mul_mx[b]);res_max = max(res_max, mx[a]*mx[b]);res_max = max(res_max, mi[a]*mi[b]);fa[b] = a;mx[a] = max(mx[a], mx[b]);mi[a] = min(mi[a], mi[b]);cnt[a] += cnt[b];mul_mx[a] = res_max;return {res_cnt, res_max};}
}ufs;
/*每个id的意义在于, id和id-1这两种酒是v相似的。也就是说,每个id实际上是把id和id-1这两种酒连起来了我要把它们放到一个并查集里面去cnt的增量等于,待合并集合的元素个数之积max的更新,max可以取得哪些值呢这个问题实际上是: 一个可以求最大乘积的并查集初始时有n个数,每个数属于自己的集合,若干次操作:选定x和y,把第x个数和第y个数所在的集合合并,返回合并后集合中任意两个数最大的乘积(不能是一个数)
*/ll pri[M]; using SA::height; //权值,height
ll ans_cnt[M], ans_max[M]; //答案计数,答案最大值vector<int> ids[M]; //ids[i]表示height=i的所有位置
//带并查集
int main(void)
{#ifdef _LITTLEFALL_freopen("in.txt","r",stdin);#endifint n = read();scanf("%s",str);SA::build(str, n, 128);for(int i=0; i<n; ++i)pri[SA::ra[i]] = read();//以下与字符串无关for(int i=2; i<=n; ++i)ids[height[i]].push_back(i); ans_max[n] = LLONG_MIN;ufs.init(n, pri);for(int v=n-1; v>=0; --v){ans_cnt[v] = ans_cnt[v+1];ans_max[v] = ans_max[v+1];for(auto id:ids[v]){auto p = ufs.join(id-1, id);ans_cnt[v] += p.first;ans_max[v] = max(ans_max[v], p.second);}}for(int v=0; v<n; ++v){if(ans_cnt[v]==0) ans_max[v] = 0;cout << ans_cnt[v] << " " << ans_max[v] << "\n";}return 0;
}inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}