BZOJ 4199 [Noi2015]品酒大会:后缀数组 + 并查集

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4199

题意:

  给你一个长度为n的字符串s,和一个长为n的数组v。

  对于每个整数r∈[0,n-1]:

    (1)问你有多少对后缀(suffix(i), suffix(j)),满足LCP(suffix(i), suffix(j)) >= r

    (2)输出mul[r] = max(v[i]*v[j]),其中i,j满足(1)的条件

 

题解:

  先考虑第(1)问。

  由于LCP只受连续的一段height中最小值的影响

  所以先将height数组排序,然后按height从大到小的顺序,合并当前height对应的两个后缀suffix(i)和suffix(j)所在的集合。

  那么对于任意两个分别属于这两个集合的后缀来说,它们的LCP一定为当前height。

  假设ans[r]表示LCP恰好为r的后缀对个数,那么此时的贡献为ans[height] += siz[find(i)] * siz[find(j)]

  最后对ans数组求一遍后缀和,即为LCP >= r的后缀对个数。

 

  然后再考虑第(2)问。

  由于v[i]有可能为负值,所以对于每个集合,维护集合中元素对应v[i]的最大值和最小值。

  那么每次合并时,用max(两个集合的最大值之积,最小值之积)更新mul[par]即可。

 

AC Code:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <algorithm>
  5 #define MAX_N 300005
  6 
  7 using namespace std;
  8 
  9 struct Height
 10 {
 11     int v,id;
 12     Height(int _v,int _id)
 13     {
 14         v=_v; id=_id;
 15     }
 16     Height(){}
 17     friend bool operator < (const Height &a,const Height &b)
 18     {
 19         return a.v<b.v;
 20     }
 21 };
 22 
 23 int n;
 24 int a[MAX_N];
 25 int sa[MAX_N];
 26 int rk[MAX_N];
 27 int t1[MAX_N];
 28 int t2[MAX_N];
 29 int cnt[MAX_N];
 30 int tsa[MAX_N];
 31 int height[MAX_N];
 32 int par[MAX_N];
 33 long long siz[MAX_N];
 34 long long maxn[MAX_N];
 35 long long minn[MAX_N];
 36 long long v[MAX_N];
 37 long long ans[MAX_N];
 38 long long mul[MAX_N];
 39 char s[MAX_N];
 40 Height h[MAX_N];
 41 
 42 void rsort()
 43 {
 44     memset(cnt,0,sizeof(cnt));
 45     for(int i=1;i<=n;i++) cnt[t2[i]]++;
 46     for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
 47     for(int i=n;i>=1;i--) tsa[cnt[t2[i]]--]=i;
 48     memset(cnt,0,sizeof(cnt));
 49     for(int i=1;i<=n;i++) cnt[t1[i]]++;
 50     for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
 51     for(int i=n;i>=1;i--) sa[cnt[t1[tsa[i]]]--]=tsa[i];
 52 }
 53 
 54 void suffix()
 55 {
 56     memset(cnt,0,sizeof(cnt));
 57     for(int i=1;i<=n;i++) a[i]=s[i],cnt[a[i]]++;
 58     for(int i='a';i<='z';i++) cnt[i]+=cnt[i-1];
 59     for(int i=1;i<=n;i++) rk[i]=cnt[a[i]];
 60     int len=1;
 61     while(len<n)
 62     {
 63         for(int i=1;i<=n;i++)
 64         {
 65             t1[i]=rk[i];
 66             t2[i]=i+len<=n ? rk[i+len] : 0;
 67         }
 68         rsort();
 69         for(int i=1;i<=n;i++)
 70         {
 71             rk[sa[i]]=rk[sa[i-1]]+(t1[sa[i]]!=t1[sa[i-1]] || t2[sa[i]]!=t2[sa[i-1]]);
 72         }
 73         len<<=1;
 74     }
 75     int k=0;
 76     for(int i=1;i<=n;i++)
 77     {
 78         k=k?k-1:k;
 79         int j=sa[rk[i]-1];
 80         while(a[i+k]==a[j+k]) k++;
 81         height[rk[i]]=k;
 82     }
 83 }
 84 
 85 void init_union_find()
 86 {
 87     for(int i=1;i<=n;i++)
 88     {
 89         par[i]=i;
 90         siz[i]=1;
 91         maxn[i]=minn[i]=v[i];
 92     }
 93 }
 94 
 95 int find(int x)
 96 {
 97     return par[x]==x ? x : par[x]=find(par[x]);
 98 }
 99 
100 void unite(int x,int y,int r)
101 {
102     int px=find(x);
103     int py=find(y);
104     ans[r]+=siz[px]*siz[py];
105     mul[r]=max(mul[r],max(maxn[px]*maxn[py],minn[px]*minn[py]));
106     siz[py]+=siz[px];
107     maxn[py]=max(maxn[py],maxn[px]);
108     minn[py]=min(minn[py],minn[px]);
109     par[px]=py;
110 }
111 
112 void read()
113 {
114     scanf("%d%s",&n,s+1);
115     for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
116 }
117 
118 void work()
119 {
120     suffix();
121     init_union_find();
122     for(int i=2;i<=n;i++) h[i]=Height(height[i],i);
123     sort(h+2,h+1+n);
124     memset(ans,0,sizeof(ans));
125     memset(mul,0x80,sizeof(mul));
126     for(int i=n;i>=2;i--) unite(sa[h[i].id],sa[h[i].id-1],h[i].v);
127     for(int i=n-1;i>=0;i--) ans[i]+=ans[i+1],mul[i]=max(mul[i],mul[i+1]);
128     for(int i=0;i<n;i++)
129     {
130         if(ans[i]) printf("%lld %lld\n",ans[i],mul[i]);
131         else printf("0 0\n");
132     }
133 }
134 
135 int main()
136 {
137     read();
138     work();
139 }

 

posted @ 2018-02-13 20:15  Leohh  阅读(217)  评论(0编辑  收藏  举报