浏览器标题切换
浏览器标题切换end
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

BZOJ3790. 神奇项链

处理出原串中每个点为中点的极长回文串,这个用hash或者manacher均可,反正这里不是复杂度瓶颈:(。那么问题就变成了可重叠的线段覆盖问题。
\(f[i]\) 表示已经完全覆盖 \(1-i\) 的最小代价
那么取结束点在i的线段\([l,r](r == i)\)
\(f[i] = \min\{f[k] + 1\} (l - 1 \leq k < r)\)
用bit维护后缀\(\min\)即可

#include <bits/stdc++.h>
using namespace std;

typedef unsigned int uint;
const int N = 100010;
const uint base = 13131;

char s[N];
uint h[N], pw[N], g[N];
int p[N], f[N], cnt, n;
struct Node {int l, r;} a[N];
struct bit {
	int c[N];
	#define lowbit(i) (i & -i)
	void clear() {
		memset(c, 0x3f, sizeof(c));
	}
	void add(int x, int v) {
		for(int i = x; i; i -= lowbit(i)) c[i] = min(c[i], v);
	}
	int query(int x) {
		int ans = 0x3f3f3f3f;
		for(int i = x; i <= n + 1; i += lowbit(i)) ans = min(ans, c[i]);
		return ans;
	}
} t;

uint gethash(int l, int r) {
	return h[r] - h[l - 1] * pw[r - l + 1];
}
uint getghash(int l, int r) {
	return g[l] - g[r + 1] * pw[r - l + 1];
}

bool check(int l, int r) {
	return gethash(l, r) == getghash(l, r);
}

bool operator < (Node a, Node b) {
	return a.r < b.r;
}

int solve() {
	t.clear();
	memset(f, 0x3f, sizeof(f));
	sort(a + 1, a + cnt + 1);
	int cur = 1;
	t.add(1, 0);
	for(int i = 1; i <= n; ++i) {
		while(cur <= cnt && a[cur].r <= i) f[i] = min(f[i], t.query(a[cur++].l) + 1);
		t.add(i + 1, f[i]);
	}
	return f[n] - 1;
}

int main() {
#ifndef ONLINE_JUDGE
freopen("data.in","r",stdin);
#endif	
	while(~scanf("%s", s + 1)) {
		n = strlen(s + 1);
		pw[0] = 1;
		h[0] = g[n + 1] = 0;
		for(int i = 1; i <= n; ++i) {
			h[i] = h[i - 1] * base + s[i];
			pw[i] = pw[i - 1] * base;
		}
		for(int i = n; i; --i) g[i] = g[i + 1] * base + s[i];
		cnt = 0;
		for(int i = 1; i <= n; ++i) {
			p[i] = 0;
			int l = 1, r = min(i - 1, n - i);
			while(l <= r) {
				int mid = (l + r) >> 1;
				if(check(i - mid, i + mid)) l = mid + 1, p[i] = mid;
				else r = mid - 1;
			}
			a[++cnt] = (Node) {i - p[i], i + p[i]};
			p[i] = 0;
			l = 0, r = min(i, n - i);
			while(l <= r) {
				int mid = (l + r) >> 1;
				if(check(i - mid + 1, i + mid)) l = mid + 1, p[i] = mid;
				else r = mid - 1;
			}
			if(p[i]) a[++cnt] = (Node) {i - p[i] + 1, i + p[i]};
		}
		printf("%d\n", solve());
	}
}
/*
处理出原串中每个点为中点的极长回文串,那么问题就变成了可重叠的线段覆盖问题。
设 $f[i]$ 表示已经完全覆盖 $1-i$ 的最小代价
那么取结束点在i的线段[l,r](r == i)
f[i] = min{f[k] + 1} (l - 1 <= k < r)
用bit维护后缀min即可
*/
posted @ 2019-11-08 10:50  henry_y  阅读(154)  评论(0编辑  收藏  举报