商品推荐走马灯(困难)

题意

  • (nle 10^5)个数,(mle 10^5)询问,每次询问区间(l_i,r_i)的所有回文串的价值和。
  • 回文串的价值为该串所有数之和。

思路

  • 将区间分成左右两个部分,左部分的回文中心会先超过左边界(l_i),右半部分的回文中心则先超过右边界(r_i)。这两个可以看成同样的问题。
  • 下面的做法只考虑左半区间。
  • 假设回文中心(p),半径为(L[p]),则(p)会和区间 ([p-L[p]+1,p]) 内的点构成回文串,对应回文串价值(前后对称,这边只写偶数长度的情况)为(2*(sum[p]-sum[x-1]),xin [p-L[p]+1,p])(sum[i]) 表示前缀和。
  • 若查询区间为 ([l_i, r_i]),则当回文中心处理到 (r_i) 对应的位置时,对应价值和为 ([l_i,r_i]) 的区间和,所以最后套个线段树即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define mp make_pair
#define sz(x) ((int)(x).size())
#define rep(i,l,r) for(int i=(l);i<(r);++i)
//-------
const int N = 1e5 + 7;
int n, m, c[N], sum[N], L[N << 1];
void manacher(int n, int a[], int l[]) {
	l[0] = 1;
	for (int i = 1, j = 0; i + 1 < n << 1; ++i) {
		int p = i >> 1, q = i - p, r = (j + 1 >> 1) + l[j] - 1;
		l[i] = r < q ? 0 : min(r - q + 1, l[(j << 1) - i]);
		while (p - l[i] != -1 && q + l[i] != n && a[p - l[i]] == a[q + l[i]])
			++l[i];
		if (q + l[i] - 1 > r)
			j = i;
	}
}
struct SegTree {
	#define ls ((t)<<1)
	#define rs ((t)<<1|1)
	int len[N << 2];
	ll f[N << 2], g[N << 2], tot[N << 2], val[N << 2];
	void build(int t, int l, int r) {
		f[t] = g[t] = tot[t] = 0, len[t] = r - l + 1;
		if (l == r) {
			val[t] = sum[l - 1];
		} else {
			int z = (l + r) >> 1;
			build(ls, l, z), build(rs, z + 1, r);
			val[t] = val[ls] + val[rs];
		}
	}
	inline void F(int t, ll add) {
		f[t] += add, tot[t] += add * len[t];
	}
	inline void G(int t, ll add) {
		g[t] += add, tot[t] += add * val[t];
	}
	inline void up(int t) {
		tot[t] = tot[ls] + tot[rs];
	}
	void down(int t) {
		if (f[t])
			F(ls, f[t]), F(rs, f[t]), f[t] = 0;
		if (g[t])
			G(ls, g[t]), G(rs, g[t]), g[t] = 0;
	}
	void upd(int t, int l, int r, int L, int R, int V, char T) {
		if (L <= l && r <= R) {
			T == 'F' ? F(t, V) : G(t, V);
			return ;
		}
		down(t);
		int z = (l + r) >> 1;
		if (L <= z)
			upd(ls, l, z, L, R, V, T);
		if (R > z)
			upd(rs, z + 1, r, L, R, V, T);
		up(t);
	}
	ll qry(int t, int l, int r, int L, int R) {
		if (L <= l && r <= R)
			return tot[t];
		down(t);
		int z = (l + r) >> 1;
		ll ret = 0;
		if (L <= z)
			ret += qry(ls, l, z, L, R);
		if (R > z)
			ret += qry(rs, z + 1, r, L, R);
		up(t);
		return ret;
	}
} seg;
ll ans[N];
vector<pair<pair<int, int>, int> > ql, qr;
void solve(vector<pair<pair<int, int>, int> > &q) {
	manacher(n, c + 1, L + 1);
	sort(q.begin(), q.end());
	rep(i, 1, n + 1)
		sum[i] = sum[i - 1] + c[i];
	seg.build(1, 1, n);
	int j = 0;
	rep(i, 1, n << 1) {
		int p = (i + 1) >> 1;
		if (L[i]) {
			seg.upd(1, 1, n, p - L[i] + 1, p, -2, 'G');
			seg.upd(1, 1, n, p - L[i] + 1, p, sum[p] + (i & 1 ? sum[p - 1] : sum[p]), 'F');
		}
		while (j < sz(q) && q[j].first.first == i) {
			ans[q[j].second] += seg.qry(1, 1, n, q[j].first.second, p);
			++j;
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	rep(i, 1, n + 1)
		scanf("%d", &c[i]);
	rep(i, 0, m) {
		int l, r;
		scanf("%d%d", &l, &r);
		// left part
		ql.pb(mp(mp((l + r) / 2 * 2 - 1, l), i));
		// right part
		qr.pb(mp(mp(2 * n - (l + r) / 2 * 2 , n + 1 - r), i));
	}
	solve(ql);
	reverse(c + 1, c + 1 + n);
	solve(qr);
	rep(i, 0, m)
		printf("%lld
", ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/mcginn/p/6659736.html