「九省联考 2018」制胡窜

Posted mangoyang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「九省联考 2018」制胡窜相关的知识,希望对你有一定的参考价值。

「九省联考 2018」制胡窜

好久没更新博客了..

考虑容斥,求切两刀后所有串都被切到至少一次的方案数。

大力分类讨论

(s) 在原串中从左到右出现的右端点序列为 (a_1 dots a_m) ,把答案分成以下三个部分

1.第一刀没有切到任意一个字符串,第二刀切完所有字符串

2.第二刀没有切到任意一个字符串,第一刀切完所有字符串

3.第一刀切完所有字符串,第二刀切到任意一个字符串

4.第一刀切到任意一个字符串且没有切完所有字符串,加上第二刀后切完所有字符串

答案就是四类方案数的和

前三类情况的方案数当一刀不能切完所有字符串时方案数之和就是 (0) ,否则方案数之和就是

[x = a_1 - a_m + len(s) - 1 x(n-a_m+a_1) - frac{x(x-1)}{2} ]

对于第四类情况,设第二刀能切的第一个串为 (p) ,不难发现 (p) 的范围是一个区间 ([L, R]) ,对于其中每一个串 (i) 作为第二刀的位置,有方案数 ((a_i-a_m+len(s)-1)min(a_i - a_{i-1}, a_1-a_{i-1}+len(s)-1))

考虑用线段树维护 ( ext{Right}) 集合,那么可以在线段树上二分出 (L, R) ,对于后面的 (min) 也有单调性,可以二分出一个位置 (p) 使得 (p) 之后都取后面的项,所以只需要在此基础上维护区间 (sum a_i(a_i-a_{i-1})) ,区间 (0,1,2) 次方和即可,复杂度 (mathcal O(nlog n))

code

/*program by mangoyang*/
#pragma GCC optimize("Ofast", "inline")
#include<bits/stdc++.h>
#define inf ((ll) 2e18)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 200005;
vector<int> ddd;
char s[N];
int n, q;
struct Node{
	int nl, nr; ll s, sz, sum, s2;
	inline Node(){ nl = nr = s = sum = sz = s2 = 0; }
	friend Node operator + (const Node &A, const Node &B){
		Node C;
		C.nl = A.nl ? A.nl : B.nl;
		C.nr = B.nr ? B.nr : A.nr;
		C.s = A.s + B.s;
		if(A.nl && B.nl) 
			C.s += 1ll * B.nl * (B.nl - A.nr);
		C.sz = A.sz + B.sz;
		C.sum = A.sum + B.sum;
		C.s2 = A.s2 + B.s2;
		return C;
	}
};
namespace Seg{
	#define mid ((l + r) >> 1)
	struct Node A[N*30];
	int sz[N*30], lc[N*30], rc[N*30], size;
	inline void ins(int &u, int l, int r, int pos){
		if(!u) u = ++size;
		if(l == r){
			sz[u] = 1, A[u].nl = A[u].nr = pos;
			A[u].sz = 1, A[u].sum = pos, A[u].s2 = 1ll * pos * pos;
			return;
		}
		if(pos <= mid) ins(lc[u], l, mid, pos);
		else ins(rc[u], mid + 1, r, pos);
		sz[u] = sz[lc[u]] + sz[rc[u]];
		A[u] = A[lc[u]] + A[rc[u]];
	}
	inline int merge(int x, int y){
		if(!x || !y) return x + y;
		int o = ++size;
		lc[o] = merge(lc[x], lc[y]);
		rc[o] = merge(rc[x], rc[y]);
		sz[o] = sz[lc[o]] + sz[rc[o]];
		A[o] = A[lc[o]] + A[rc[o]];
		return o;
	}
	inline int queryL(int u, int l, int r, int k){
		if(l == r) return A[u].nr + k > 0 ? l : n + 1;
		if(A[lc[u]].nr + k > 0) 
			return queryL(lc[u], l, mid, k);
		else return queryL(rc[u], mid + 1, r, k);
	}
	inline int queryR(int u, int l, int r, int k){
		if(l == r) return l;
		if((sz[lc[u]] && k - A[lc[u]].nr <= 0) || !sz[rc[u]])
			return queryR(lc[u], l, mid, k);
		else return queryR(rc[u], mid + 1, r, k); 
	}
	inline Node query(int u, int l, int r, int L, int R){
		if(l >= L && r <= R) return A[u];
		if(L > mid) return query(rc[u], mid + 1, r, L, R);
		if(R <= mid) return query(lc[u], l, mid, L, R);
		return query(lc[u], l, mid, L, R) + query(rc[u], mid + 1, r, L, R);
	}
	inline int queryGG(int u, int l, int r, int k){
		if(l == r) return l > k ? l : n + 1;
		if(A[lc[u]].nr > k) return queryGG(lc[u], l, mid, k);
		else return queryGG(rc[u], mid + 1, r, k);
	}
}
namespace sam{
	vector<int> g[N];
	int ch[N][10], fa[N], pos[N], rt[N], f[N][21], len[N], size = 1, tail = 1;
	inline int newnode(int x){ return len[++size] = x, size; }
	inline void ins(int c, int po){
		int p = tail, np = newnode(len[p] + 1);
		Seg::ins(rt[np], 1, n, po), pos[po] = np;
		for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
		if(!p) return (void) (tail = np, fa[np] = 1);
		int q = ch[p][c];
		if(len[q] == len[p] + 1) fa[np] = q;
		else{
			int nq = newnode(len[p] + 1);
			fa[nq] = fa[q], fa[q] = fa[np] = nq;
			for(int i = 0; i < 10; i++) ch[nq][i] = ch[q][i];
			for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
		}tail = np;
	}
	inline void addedge(){
		for(int i = 2; i <= size; i++) g[fa[i]].push_back(i);
	}
	inline void dfs(int u){
		for(int i = 1; i <= 20; i++) f[u][i] = f[f[u][i-1]][i-1];
		for(auto v : g[u]){ 
			f[v][0] = u, dfs(v);
			rt[u] = Seg::merge(rt[u], rt[v]);
		}
	}
	inline int getnode(int l, int r){
		int u = pos[r];
		for(int i = 20; ~i; i--) 
			if(len[f[u][i]] >= r - l + 1) u = f[u][i];
		return u;
	}
	inline ll solve(int l, int r){
		int x = r - l + 1, u = getnode(l, r);
		int nl = Seg::A[rt[u]].nl;
		int nr = Seg::A[rt[u]].nr;
		ll res = 0;
		if(nl - nr + x - 1 > 0){
			res += 1ll * (nl - nr + x - 1) * (n - nr + nl - 2);
			res -= 1ll * (nl - nr + x - 1) * (nl - nr + x - 2) / 2;
		}
		int L = Seg::queryL(rt[u], 1, n, x - nr - 1);
		int R = Seg::queryR(rt[u], 1, n, x + nl - 1);
		if(L > R) return 0;
		int pos = max(L + 1, Seg::queryGG(rt[u], 1, n, nl + x - 1));
		Node now;
		if(R >= pos) now = Seg::query(rt[u], 1, n, pos, R);
		res -= now.s2;
		res += 1ll * (R - L) * (x - nr - 1);
		res += Seg::query(rt[u], 1, n, L, R).s;
		res += now.sum * (nl + x - 1);
		res -= now.sum * (x - nr - 1);
		res += (nl + x - 1) * now.sz * (x - nr - 1);
		if(L > R) return 0;
		if(L > nl){ 
			int p = Seg::query(rt[u], 1, n, 1, L - 1).nr;
			res += 1ll * min(L - p, nl - p + x - 1) * (L + x - nr - 1);
		}
		return res;
	}
}
int main(){
	read(n), read(q);
	scanf("%s", s + 1);	
	for(int i = 1; i <= n; i++) sam::ins(s[i] - ‘0‘, i);
	sam::addedge();
	sam::dfs(1);
	while(q--){
		int L, R; read(L), read(R);
		if(n <= 2){ puts("0"); continue; } 
		ll res = 1ll * n * (n - 1) / 2 - (n - 1);
		res -= sam::solve(L, R);
		printf("%lld
", res);
	}
	return 0;
}	

以上是关于「九省联考 2018」制胡窜的主要内容,如果未能解决你的问题,请参考以下文章

HEOI 2018制胡窜

P2341 [HAOI2006]受欢迎的牛

p4363 [九省联考2018]一双木棋chess

[九省联考2018]秘密袭击coat 伪·题解

[九省联考 2018]IIIDX

loj2472 「九省联考 2018」IIIDX