BZOJ4199: [Noi2015]品酒大会

n<=300000的串,每一位有权<=1e9,对每个r=0~n-1问lcp长度为r的两个后缀有多少种,并在其中找出一个二元后缀使得他们开头的字符对应权值乘起来最大。

n*n*n:略

n*n*logn:哈希,略

lcp长度为指定长度我不会,但longes common suffix--最长公共后缀,这不是SAM的经典用途吗?

反过来构造出SAM后,一个状态对答案的贡献即其对应parent树的子树中找两个数相乘得到的最大值,以及这个状态对应的串的数量,贡献的范围是这个状态的[Min,Max],上面变量意义见CLJ论文。一个状态对应的串的数量可以在parent树中自底向上递推得,而两个数相乘的最大值呢?其实就是记子树最大次大最小次小值啦,因为一个数列中两个数相乘的最大值,要么是两个最大数相乘,要么是两个最小数相乘。

在一个区间里把答案加上某个数可以差分。而区间取Max呢?可以发现parent树中上面的 “两数相乘最大值”一定比下面的大,所以可以把一个修改当成一个前缀修改。比如说长度[3,7]的状态,由于3相似,4相似,……,7相似的两个串同时也是1相似2相似的,所以相当于把[1,7]的状态取个Max。最后查每个数是多少,只要把每次修改当一个标记,查这个点后面的标记的最大值即可。

  1 #include<stdio.h>
  2 #include<algorithm>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 //#include<queue>
  6 //#include<iostream>
  7 using namespace std;
  8 
  9 int n;
 10 #define LL long long
 11 #define maxn 600011
 12 char s[maxn];int val[maxn];
 13 
 14 struct SAM
 15 {
 16     struct Node
 17     {
 18         int ch[26],pos,pre,Max1,Max2,Min1,Min2,cnt;LL val,tot; bool vis;
 19         Node() {memset(ch,0,sizeof(ch)); cnt=0; pre=0;}
 20     }a[maxn];
 21     int root,last,size;
 22     SAM() {root=last=size=1; a[1].pos=0; a[0].pos=-1;}
 23     int idx(char c) {return c-'a';}
 24     void insert(char c,int p)
 25     {
 26         int id=idx(c),x=++size;
 27         a[x].pos=p; a[x].cnt=1;
 28         int y=last;
 29         for (;y && !a[y].ch[id];y=a[y].pre) a[y].ch[id]=x;
 30         last=x;
 31         if (!y) a[x].pre=root;
 32         else if (a[a[y].ch[id]].pos==a[y].pos+1) a[x].pre=a[y].ch[id];
 33         else
 34         {
 35             int z=a[y].ch[id],w=++size;
 36             a[w]=a[z]; a[w].pos=a[y].pos+1; a[w].cnt=0;
 37             a[z].pre=a[x].pre=w;
 38             for (;y && a[y].ch[id]==z;y=a[y].pre) a[y].ch[id]=w;
 39         }
 40     }
 41     struct Edge{int to,next;}edge[maxn];int first[maxn],le;
 42     void in(int x,int y) {Edge &e=edge[le];e.to=y;e.next=first[x];first[x]=le++;}
 43     
 44     void dfs(int x)
 45     {
 46         if (a[x].cnt==1) a[x].Max1=a[x].Min1=val[a[x].pos];
 47         else a[x].Max1=-0x3f3f3f3f,a[x].Min1=0x3f3f3f3f;
 48         a[x].Min2=0x3f3f3f3f,a[x].Max2=-0x3f3f3f3f;
 49         for (int i=first[x];i;i=edge[i].next)
 50         {
 51             const Edge &e=edge[i];
 52             dfs(e.to);
 53             a[x].cnt+=a[e.to].cnt;
 54             if (a[e.to].Max1>a[x].Max1)
 55             {
 56                 a[x].Max2=a[x].Max1;
 57                 a[x].Max1=a[e.to].Max1;
 58             }
 59             else if (a[e.to].Max1>a[x].Max2) a[x].Max2=a[e.to].Max1;
 60             if (a[e.to].Max2>a[x].Max2) a[x].Max2=a[e.to].Max2;
 61             if (a[e.to].Min1<a[x].Min1)
 62             {
 63                 a[x].Min2=a[x].Min1;
 64                 a[x].Min1=a[e.to].Min1;
 65             }
 66             else if (a[e.to].Min1<a[x].Min2) a[x].Min2=a[e.to].Min1;
 67             if (a[e.to].Min2<a[x].Min2) a[x].Min2=a[e.to].Min2;
 68         }
 69         a[x].tot=1ll*a[x].cnt*(a[x].cnt-1)/2;
 70         a[x].val=a[x].Max2==-0x3f3f3f3f?(LL)(-1e18)-1:max(a[x].Max1*1ll*a[x].Max2,a[x].Min1*1ll*a[x].Min2);
 71     }
 72         
 73     void buildtree()
 74     {
 75         le=2;
 76         for (int i=2;i<=size;i++) in(a[i].pre,i);
 77         dfs(1);
 78     }
 79 }sam;
 80 
 81 struct BIT
 82 {
 83     LL a[maxn];int n;
 84     void clear(int m) {n=m;for (int i=1;i<=n;i++) a[i]=(LL)(-1e18)-1;}
 85     void add(int x,LL v) {for (;x<=n;x+=x&-x) a[x]=max(a[x],v);}
 86     LL query(int x) {LL ans=(LL)(-1e18)-1;for (;x;x-=x&-x) ans=max(ans,a[x]);return ans;}
 87 }t;
 88 LL tag[maxn];
 89 int main()
 90 {
 91     scanf("%d",&n);
 92     scanf("%s",s+1);
 93     for (int i=1;i<=n/2;i++) swap(s[i],s[n-i+1]);
 94     for (int i=n;i;i--) scanf("%d",&val[i]);
 95     for (int i=1;i<=n;i++) sam.insert(s[i],i);
 96     
 97     sam.buildtree();
 98     t.clear(n+1);
 99     for (int i=1;i<=sam.size;i++)
100     {
101         t.add((n+1)-(sam.a[i].pos+1)+1,sam.a[i].val);
102         tag[sam.a[sam.a[i].pre].pos+1]+=sam.a[i].tot;
103         tag[sam.a[i].pos+1]-=sam.a[i].tot;
104     }
105     LL tmp;
106     for (int i=1;i<=n;i++)
107     {
108         printf("%lld %lld\n",tag[i-1],(tmp=t.query(n-i+2))==(LL)(-1e18)-1?0:tmp);
109         tag[i]+=tag[i-1];
110     }
111     return 0;
112 }
View Code

 

posted @ 2017-12-11 19:41  Blue233333  阅读(188)  评论(0编辑  收藏  举报