zoukankan      html  css  js  c++  java
  • BZOJ4199[NOI2015]品酒大会(后缀数组/后缀自动机+线段树)

    题目链接

    洛谷

    UOJ

    BZOJ

    UOJ上有组hack数据值得一交

    解析

    后缀数组或后缀自动机,以下是后缀自动机我依然不会后缀数组你敢信……

    容易发现把原串翻转后"(r)相似"就是两个前缀的最长公共后缀长度不小于(r)

    于是想到后缀自动机

    后缀自动机上一个节点(endpos/right)集合的大小就是满足"(r)相似"的位置个数,这个节点(p)可以更新的(r)满足(maxlen_{link(p)} + 1 le r le maxlen_p),可以线段树维护

    于是在(link)树/(parent)树上(dp)求出每个节点最大值、次大值、最小值、次小值(因为权值可能为负),以及(endpos)集合的大小即可统计答案

    注意特判(ans1)为零的时候(ans2)也为零

    代码

    因为改得比较多,所以有点乱

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define MAXN 300005
    
    typedef long long LL;
    const int inf = 0x3f3f3f3f;
    struct SAM {
    	int idx, last, link[MAXN << 1], maxlen[MAXN << 1], next[MAXN << 1][26], size[MAXN << 1], deg[MAXN << 1];
    	int mx1[MAXN << 1], mx2[MAXN << 1], mn1[MAXN << 1], mn2[MAXN << 1];
    	int newnode();
    	int newnode(int);
    	void build();
    	int add(int);
    	void work();
    };
    struct SegmentTree {
    	LL maxv[MAXN << 2], add[MAXN << 2];
    	void build(int, int, int);
    	void update(int, int, int, int, int, LL, LL);
    	void query(int, int, int, int, LL &, LL &);
    };
    
    int N, a[MAXN];
    LL ans1, ans2;
    char str[MAXN];
    SAM sam;
    SegmentTree sgt;
    
    int main() {
    	//freopen("test.in", "r", stdin);
    	//freopen("test.out", "w", stdout);
    	
    	scanf("%d%s", &N, str);
    	std::reverse(str, str + N);
    	for (int i = N - 1; i >= 0; --i) scanf("%d", a + i);
    	sam.build();
    	sam.work();
    	ans1 = (LL)N * (N - 1), ans2 = std::max((LL)sam.mx1[1] * sam.mx2[1], (LL)sam.mn1[1] * sam.mn2[1]);
    	if (!ans1) ans2 = 0;
    	printf("%lld %lld
    ", ans1 / 2, ans2);
    	for (int i = 1; i < N; ++i) {
    		ans1 = 0, ans2 = -0x3f3f3f3f3f3f3f3f;
    		sgt.query(1, 1, N, i, ans1, ans2);
    		if (!ans1) ans2 = 0;
    		printf("%lld %lld
    ", ans1 / 2, ans2);
    	}
    	
    	return 0;
    }
    void SAM::build() {
    	idx = 0, last = newnode();
    	for (int i = 0; i < N; ++i) {
    		last = add(str[i] - 'a');
    		mx1[last] = mn1[last] = a[i];
    		mx2[last] = -inf, mn2[last] = inf;
    		size[last] = 1;
    	}
    	for (int i = 0; i <= idx; ++i) if (!size[i]) mn1[i] = mn2[i] = inf, mx1[i] = mx2[i] = -inf;
    }
    int SAM::add(int c) {
    	int np = newnode(), p = last;
    	maxlen[np] = maxlen[last] + 1;
    	while (p && !next[p][c]) next[p][c] = np, p = link[p];
    	if (!p) link[np] = 1;
    	else {
    		int q = next[p][c];
    		if (maxlen[q] == maxlen[p] + 1) link[np] = q;
    		else {
    			int nq = newnode(q);
    			maxlen[nq] = maxlen[p] + 1;
    			link[q] = link[np] = nq;
    			while (p && next[p][c] == q) next[p][c] = nq, p = link[p];
    		}
    	}
    	return np;
    }
    int SAM::newnode() { return ++idx; }
    int SAM::newnode(int x) {
    	++idx;
    	maxlen[idx] = maxlen[x], link[idx] = link[x];
    	for (int i = 0; i < 26; ++i) next[idx][i] = next[x][i];
    	return idx;
    }
    void SAM::work() {
    	static int q[MAXN << 1], hd, tl;
    	for (int i = 1; i <= idx; ++i) ++deg[link[i]];
    	for (int i = 1; i <= idx; ++i) if (!deg[i]) q[tl++] = i;
    	while (hd < tl) {
    		int p = q[hd++], fa = link[p];
    		size[fa] += size[p];
    		if (mx1[p] >= mx1[fa]) mx2[fa] = std::max(mx1[fa], mx2[p]), mx1[fa] = mx1[p];
    		else mx2[fa] = std::max(mx2[fa], mx1[p]);
    		if (mn1[p] <= mn1[fa]) mn2[fa] = std::min(mn1[fa], mn2[p]), mn1[fa] = mn1[p];
    		else mn2[fa] = std::min(mn2[fa], mn1[p]);
    		if (!(--deg[fa])) q[tl++] = fa;
    	}
    	sgt.build(1, 1, N);
    	for (int i = 2; i <= idx; ++i) {
    		bool t = 0;
    		if (mx1[i] != -inf && mx2[i] != -inf)
    			sgt.update(1, 1, N, maxlen[link[i]] + 1, maxlen[i], size[i], (LL)mx1[i] * mx2[i]), t = 1;
    		if (mn1[i] != inf && mn2[i] != inf)
    			sgt.update(1, 1, N, maxlen[link[i]] + 1, maxlen[i], t ? 0 : size[i], (LL)mn1[i] * mn2[i]);
    	}
    			
    }
    void SegmentTree::update(int rt, int L, int R, int l, int r, LL sz, LL v) {
    	if (L >= l && R <= r) add[rt] += sz * (sz - 1), maxv[rt] = std::max(maxv[rt], v);
    	else {
    		int mid = (L + R) >> 1;
    		if (l <= mid) update(rt << 1, L, mid, l, r, sz, v);
    		if (r > mid) update(rt << 1 | 1, mid + 1, R, l, r, sz, v);
    	}
    }
    void SegmentTree::query(int rt, int L, int R, int pos, LL &res1, LL &res2) {
    	res1 += add[rt], res2 = std::max(res2, maxv[rt]);
    	if (L == R) return;
    	int mid = (L + R) >> 1;
    	if (pos <= mid) query(rt << 1, L, mid, pos, res1, res2);
    	else query(rt << 1 | 1, mid + 1, R, pos, res1, res2);
    }
    void SegmentTree::build(int rt, int L, int R) {
    	maxv[rt] = -0x3f3f3f3f3f3f3f3f, add[rt] = 0;
    	if (L == R) return;
    	int mid = (L + R) >> 1;
    	build(rt << 1, L, mid);
    	build(rt << 1 | 1, mid + 1, R);
    }
    //Rhein_E
    
  • 相关阅读:
    Java反射中Class.forName与classLoader的区别
    Java各种成员初始化顺序
    crontab python脚本不执行
    Java mybatis缓存(转)
    Java Synchronized及实现原理
    JVM类加载器
    SSH掉线问题
    SSH登陆远程卡、慢的解决的办法
    shell脚本执行python脚本时,python如何将返回值传给shell脚本
    使用scrapy进行数据爬取
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10643202.html
Copyright © 2011-2022 走看看