zoukankan      html  css  js  c++  java
  • 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
    ",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
  • 相关阅读:
    Medium | LeetCode 179. 最大数 | 排序
    Medium | LeetCode 448. 找到所有数组中消失的数字 | 原地Hash
    Medium | LeetCode 78. 子集 | 回溯
    Medium | LeetCode 39. 组合总和 | 回溯
    js上传头像进行图片压缩
    js 点击按钮复制文本
    eclipse下载
    gulp打包修改配置文件
    vuejs之过滤器(filters)的使用
    关于layui弹出层关闭之后,弹出的dom显示问题
  • 原文地址:https://www.cnblogs.com/Blue233333/p/8024734.html
Copyright © 2011-2022 走看看