传送门


感觉题目讲的很不清楚……

题目意思就是给出一个长度为\(n\)的字符串,求对于\(r=0,1,...,n-1\),求出\(LCP(suffix_p,suffix_q) \geq r\)的无序数对\((p,q)\)的数目,并令一对无序数对的价值为\(val_p \times val_q\),则还要求对于每一个\(r\),所有满足上述条件的无序数对中的最大价值

跟后缀\(LCP\)长度有关,直接上\(SA\)。求出\(sa\)数组和\(height\)数组,我们考虑如何实现对于每一个\(r\)的询问快速求出答案。不妨将\(r\)从大到小求解,那么对于某一个后缀\(sa_k\),满足\(LCP(suffix_{sa_p} , suffix_{sa_k}) \geq r\)的\(p\)一定是一段区间,而且这一段区间随着\(r\)的缩小不断增大。

然后我们考虑如何拓展区间。考虑对于\(height_k=q\),当\(r>q\)的时候\(k\)位置两端的区间不会越过\(k-1\)与\(k\),而当\(r \leq q\)时这两段区间就会合成一段区间。这个显然是可以使用并查集维护的,并且可以比较轻松地在并查集上维护最大价值。

#include<bits/stdc++.h>
#define mid ((l + r) >> 1)
#define lch Tree[x].l
#define rch Tree[x].r
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ '0');
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 3e5 + 10;
int fa[MAXN] , val[MAXN] , valMax[MAXN][2] , valMin[MAXN][2];
int sa[MAXN] , rk[MAXN] , pot[MAXN] , tp[MAXN << 1] , h[MAXN];
int ind[MAXN] , size[MAXN] , N , maxN = 26;
char s[MAXN];
long long Max , cnt , ans[MAXN][2];

int find(int x){
    return fa[x] == x ? x : (fa[x] = find(fa[x]));
}

void Debug(){
    for(int i = 1 ; i <= N ; ++i)
        cout << sa[i] << ' ';
    cout << endl;
    for(int i = 1 ; i <= N ; ++i)
        cout << ind[i] << ' ';
    cout << endl << endl;
}

void input(){
    N = read();
    scanf("%s" , s + 1);
    for(int i = 1 ; i <= N ; ++i){
        val[i] = read();
        if(val[i] < 0)
            valMin[i][0] = val[i];
    }
}

void sort(int p){
    memset(pot , 0 , sizeof(pot));
    for(int i = 1 ; i <= N ; ++i)
        ++pot[rk[i]];
    for(int i = 1 ; i <= maxN ; ++i)
        pot[i] += pot[i - 1];
    for(int i = 1 ; i <= N ; ++i)
        sa[++pot[rk[tp[i]] - 1]] = tp[i];
    memcpy(tp , rk , sizeof(int) * (N + 1));
    for(int i = 1 ; i <= N ; ++i)
        rk[sa[i]] = rk[sa[i - 1]] + (tp[sa[i]] != tp[sa[i - 1]] || tp[sa[i] + p] != tp[sa[i - 1] + p]);
    maxN = rk[sa[N]];
}

bool cmp(int a , int b){
    return h[a] < h[b];
}

void init(){
    memset(valMax , -0x3f , sizeof(valMax));
    Max = -1ll * 0x3f3f3f3f * 0x3f3f3f3f;
    for(int i = 1 ; i <= N ; ++i)
        rk[tp[i] = i] = s[i] - 'a' + 1;
    sort(0);
    for(int i = 1 ; i <= N && maxN < N ; i <<= 1){
        int cnt = 0;
        for(int j = 1 ; j <= i ; ++j)
            tp[++cnt] = N - i + j;
        for(int j = 1 ; j <= N ; ++j)
            if(sa[j] > i)
                tp[++cnt] = sa[j] - i;
        sort(i);
    }
    for(int i = 1 ; i <= N ; ++i){
        if(rk[i] == 1)
            continue;
        int t = rk[i];
        h[t] = max(0 , h[rk[i - 1]] - 1);
        while(s[sa[t] + h[t]] == s[sa[t - 1] + h[t]])
            ++h[t];
        ind[t] = t;
    }
    sort(ind + 2 , ind + N + 1 , cmp);
    for(int i = 1 ; i <= N ; ++i){
        fa[i] = i;
        size[i] = 1;
        valMax[i][0] = val[i];
    }
}

inline void merge(int x , int y){
    fa[x] = y;
    int num[4] = {valMax[x][0] , valMax[x][1] , valMax[y][0] , valMax[y][1]};
    sort(num , num + 4);
    valMax[y][0] = num[3];
    valMax[y][1] = num[2];
    Max = max(Max , 1ll * valMax[y][0] * valMax[y][1]);
    num[0] = valMin[x][0];
    num[1] = valMin[x][1];
    num[2] = valMin[y][0];
    num[3] = valMin[y][1];
    sort(num , num + 4);
    valMin[y][0] = num[0];
    valMin[y][1] = num[1];
    if(1ll * valMin[y][0] * valMin[y][1])
        Max = max(Max , 1ll * valMin[y][0] * valMin[y][1]);
    cnt -= 1ll * size[x] * (size[x] - 1) / 2 + 1ll * size[y] * (size[y] - 1) / 2;
    size[y] += size[x];
    cnt += 1ll * size[y] * (size[y] - 1) / 2;
}

void work(){
    int p = N;
    for(int i = N - 1 ; i >= 0 ; --i){
        while(p > 1 && h[ind[p]] == i){
            merge(find(sa[ind[p]]) , find(sa[ind[p] - 1]));
            --p;
        }
        if(cnt){
            ans[i][0] = cnt;
            ans[i][1] = Max;
        }
    }
}

void output(){
    for(int i = 0 ; i <= N - 1 ; ++i)
        cout << ans[i][0] << ' ' << ans[i][1] << '\n';
}

int main(){
    input();
    init();
    work();
    output();
    return 0;
}

Luogu2178 NOI2015 品酒大会 SA、并查集的更多相关文章

  1. bzoj4199: [Noi2015]品酒大会 (并查集 && 后缀数组)

    据说用后缀自动机 + dp也能做 然而并不会 后缀数组的做法呢 就是先建个后缀数组,求出height值,此时如果直接找,复杂度是n ^ 2的,肯定会超时. 但是height大的值是不会对小的产生影响的 ...

  2. [NOI2015]品酒大会(SA数组)

    [NOI2015]品酒大会 题目描述 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发"首席品酒家"和" ...

  3. luogu2178/bzoj4199 品酒大会 (SA+单调栈)

    他要求的就是lcp(x,y)>=i的(x,y)的个数和a[x]*a[y]的最大值 做一下后缀和,就只要求lcp=i的了 既然lcp(x,y)=min(h[rank[x]+1],..,[h[ran ...

  4. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

  5. [UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集

    [UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...

  6. 【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集

    [BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默 ...

  7. [NOI2015] 品酒大会 - 后缀数组,并查集,STL,启发式合并

    [NOI2015] 品酒大会 Description 对于每一个 \(i \in [0,n)\) 求有多少对后缀满足 LCP 长度 \(\le i\) ,并求满足条件的两个后缀权值乘积的最大值. So ...

  8. [UOJ#131][BZOJ4199][NOI2015]品酒大会

    [UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...

  9. 洛谷 P2178 [NOI2015]品酒大会 解题报告

    P2178 [NOI2015]品酒大会 题目描述 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发"首席品酒家"和 ...

随机推荐

  1. video自动禁止全屏

    在微信浏览器.苹果等其他浏览器,里面使用video标签,会自动变成全屏,改成下面就好了,起码可以在video标签之上加入其他元素    webkit-playsinline playsinline x ...

  2. JavaSE——UDP协议网络编程(二)

    在 UDP 网络编程中,发送方与接收方没有建立联系,没有明显的服务器端和客户端的区别. 类 DatagramSocket: 此类表示用来发送和接收数据报包的套接字. 主要的构造方法: Datagram ...

  3. 让 Odoo POS 支持廉价小票打印机

    为了测试 Odoo 在实际业务中的实施,我们开了一家(马上要开第二家分店)猪肉店.由于预算有限,在实施 Odoo PoS 的时候采购了一台价格为 85 元的爱宝热敏打印机,结果连上 Odoo Posb ...

  4. 语义分割的简单指南 A Simple Guide to Semantic Segmentation

    语义分割是将标签分配给图像中的每个像素的过程.这与分类形成鲜明对比,其中单个标签被分配给整个图片.语义分段将同一类的多个对象视为单个实体.另一方面,实例分段将同一类的多个对象视为不同的单个对象(或实例 ...

  5. Oracle 启动实例(instance)、打开数据库

    Oracle启动实例(instance).打开数据库   by:授客 QQ:1033553122 启动实例(instance).打开数据库 1.开启sqlplus [laiyu@localhost ~ ...

  6. id、name、setter方法注入、构造方法注入、工厂方法注入、注解注入、方法注入、方法替换、Web作用域、普通bean引用Web作用域的bean

    spring IoC的id和name id的命名需要满足XML对id的命名规范,必须以字母开始,后面可以是字母.数字.连字符.下画线.句号.冒号等等号,但逗号和空格是非法的.如果用户确实希望用一些特殊 ...

  7. Expo大作战(四十一)【完】--expo sdk 之 Assets,BarCodeScanner,AppLoading

    Expo大作战系列完结! 简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与 ...

  8. 利用Spring的AbstractRoutingDataSource解决多数据源的问题

    多数据源问题很常见,例如读写分离数据库配置. 原来的项目出现了新需求,局方要求新增某服务器用以提供某代码,涉及到多数据源的问题. 解决方法如下: 1.首先配置多个datasource <bean ...

  9. 第七章 鼠标(CHECKER1)

    CHECKER1程序将客户区划分成25个矩形,构成一个5*5的数组.如果在其中一个矩形内单击鼠标,就用X形填充该矩形.再次单击,则X形消失. /*--------------------------- ...

  10. MATLAB矩阵的LU分解及在解线性方程组中的应用

    作者:凯鲁嘎吉 - 博客园http://www.cnblogs.com/kailugaji/ 三.实验程序 五.解答(按如下顺序提交电子版) 1.(程序) (1)LU分解源程序: function [ ...