zoukankan      html  css  js  c++  java
  • 【LOJ】#2133. 「NOI2015」品酒大会

    题解

    想出了一个神奇的技巧

    我们先把串反过来(因为我们需要起始位置的值而不是终止位置的值),每个点维护一下 fail树上子树里的点,作为正数绝对值最大的两个数,作为负数绝对值最大的两个数

    我们发现这个两两相乘的最大值肯定是一个后缀最大值,我们对每个节点求一个最大值存到这个节点的len处理出后缀最大值就行

    然后关键是怎么求这个多少个位置两两匹配前缀是0,1,2....n - 1

    似乎会想到把后缀自动机上节点的cnt累加进去就好?然而,这样是错的,你连第一个样例都过不了
    为什么呢,我们把样例第一个串反过来之后看一看
    ponoiiipoi

    iopiiionop

    你如果要统计两个p的位置作为相同长度为1的两个位置
    非常尴尬,没有这个节点

    怎么办,你BFS显然会超时……为什么i这个位置就会被统计进去……因为有这个节点
    ii这个节点的par就是i

    经过一番大胆的猜想,发现我们需要把([p->par->len + 1,p->len])这一段的答案要加上(p->cnt *(p->cnt - 1) / 2)

    那么为什么这样是正确的呢……显然= =
    因为p->par就是p的一段后缀,这段后缀显然会累加上由于最长匹配是个长串带来的贡献,而中间的一段也是这个长串的一部分,没有被加上

    代码

    #include <bits/stdc++.h>
    //#define ivorysi
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define eps 1e-8
    #define mo 974711
    #define MAXN 300005
    #define pii pair<int,int>
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {putchar('-');x = -x;}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    struct node {
        int len,cnt;
        int val[2][2],vc[2];
        node *nxt[26],*par;
    }pool[MAXN * 2],*tail = pool,*root,*last,*que[MAXN * 2];
    int a[MAXN],N,c[MAXN];
    int64 ans[MAXN],sum[MAXN],d[MAXN];
    char str[MAXN];
    void build_SAM(int e,int len,int v) {
        node *nowp = tail++,*p;
        nowp->len = len;nowp->cnt = 1;
        if(v < 0) nowp->val[0][nowp->vc[0]++] = v;
        else nowp->val[1][nowp->vc[1]++] = v;
        for(p = last ; p && !p->nxt[e] ; p = p->par) {
    	p->nxt[e] = nowp;
        }
        if(!p) nowp->par = root;
        else {
    	node *q = p->nxt[e];
    	if(q->len == p->len + 1) nowp->par = q;
    	else {
    	    node *copyq = tail++;
    	    *copyq = *q;
    	    copyq->vc[0] = copyq->vc[1] = 0;
    	    memset(copyq->val,0,sizeof(copyq->val));
    	    copyq->cnt = 0;
    	    copyq->len = p->len + 1;
    	    q->par = nowp->par = copyq;
    	    for(; p && p->nxt[e] == q ; p = p->par) p->nxt[e] = copyq;
    	}
        }
        last = nowp;
    }
    void update(node *p,int on,int v) {
        if(p->vc[on] < 2) {p->val[on][1] = v;++p->vc[on];}
        else {
    	if(abs(v) > abs(p->val[on][1])) p->val[on][1] = v;
        }
        if(abs(p->val[on][1]) > abs(p->val[on][0])) swap(p->val[on][1],p->val[on][0]);
    }
    void Init() {
        read(N);scanf("%s",str + 1);
        for(int i = 1 ; i <= N ; ++i) read(a[i]);
        for(int i = 1 ; i <= N / 2 ; ++i) {
    	swap(a[i],a[N - i + 1]);swap(str[i],str[N - i + 1]);
        }
        root = last = tail++;
        for(int i = 1 ; i <= N ; ++i) build_SAM(str[i] - 'a',i,a[i]);
    }
    void Solve() {
        int m = tail - pool;
        for(int i = 0 ; i < m ; ++i) c[pool[i].len]++;
        for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
        for(int i = 0 ; i < m ; ++i) que[c[pool[i].len]--] = &pool[i];
        for(int i = 0 ; i <= N ; ++i) ans[i] = -1e18;
        for(int i = m ; i >= 1 ; --i) {
    	if(que[i]->cnt > 1) {
    	    sum[que[i]->len] += 1LL * (que[i]->cnt - 1) * (que[i]->cnt) / 2;
    	    if(que[i]->vc[0] >= 2) ans[que[i]->len] = max(ans[que[i]->len],1LL * que[i]->val[0][1] * que[i]->val[0][0]);
    	    if(que[i]->vc[1] >= 2) ans[que[i]->len] = max(ans[que[i]->len],1LL * que[i]->val[1][0] * que[i]->val[1][1]);
    	    for(int k = 0 ;k < que[i]->vc[0] ; ++k) {
    		for(int h = 0 ; h < que[i]->vc[1] ; ++h) {
    		    ans[que[i]->len] = max(ans[que[i]->len],1LL * que[i]->val[0][k] * que[i]->val[1][h]);
    		}
    	    }
    	}
    	d[que[i]->len - 1] += 1LL * (que[i]->cnt - 1) * que[i]->cnt / 2;
    	if(que[i]->par) {
    	    que[i]->par->cnt += que[i]->cnt;
    	    for(int k = 0 ; k < que[i]->vc[0] ; ++k) {
    		update(que[i]->par,0,que[i]->val[0][k]);
    	    }
    	    for(int k = 0 ; k < que[i]->vc[1] ; ++k) {
    		update(que[i]->par,1,que[i]->val[1][k]);
    	    }
    	    d[que[i]->par->len] -= 1LL * (que[i]->cnt - 1) * que[i]->cnt / 2;
    	}
    	
    	
        }
        for(int i = N ; i >= 1 ; --i) d[i] += d[i + 1],sum[i] += d[i];
        for(int i = N - 1; i >= 1 ; --i) ans[i] = max(ans[i + 1],ans[i]);
        for(int i = 0 ; i <= N - 1 ; ++i) {
    	if(!sum[i]) ans[i] = 0;
    	out(sum[i]);space;out(ans[i]);enter;
        }
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    
  • 相关阅读:
    keepalived 结合mysql 自动切换
    haproxy 配置日志
    haproxy 配置日志
    keepalive的 nopreempt 非抢占
    keepalive的 nopreempt 非抢占
    keepavlied一些参数
    keepavlied一些参数
    mysql 结合keepalived测试
    【转载】[小红猪]11个物理难题,11种基本粒子 分类: 生活百科 2013-07-26 11:04 317人阅读 评论(0) 收藏
    【转载】上帝粒子证实存在宇宙末日来临?(图) 分类: 生活百科 2013-07-26 11:04 336人阅读 评论(0) 收藏
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9167678.html
Copyright © 2011-2022 走看看