zoukankan      html  css  js  c++  java
  • 「九省联考 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;
    }	
    
  • 相关阅读:
    第六章 装饰模式
    第二章 策略模式
    第一章 简单工厂模式
    HTTPS-post请求
    import&export
    Flask(Jinja2) 服务端模板注入漏洞vulhub
    MySQL UDF提权 过程及注意事项
    centos7 安装jdk1.8.0_271 以及错误解决
    WEB、FTP服务器所有响应码解释(超详细)
    Wolf CMS后台文件上传getshell并提权
  • 原文地址:https://www.cnblogs.com/mangoyang/p/12939699.html
Copyright © 2011-2022 走看看