• 【LOJ】#2479. 「九省联考 2018」制胡窜


    题解

    老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很

    现在后缀树上线段树合并差点把我写死

    主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了

    但就是不想写

    但是由于我过于老年化,我还是决定记录一下我的思路

    我用后缀自动机建的后缀树,所以是反串的后缀树,我考虑的都是区间字符串的结束位置
    先熟练的建一个后缀树,我们对于每个区间代表的字符串,可以在后缀树上找到对应的节点,这个节点的子树里包含的结束位置往前数(R - L + 1)就是所有的这个区间字符串出现的位置

    我们考虑反着来,好容易啊只需要统计三个区间都没有的方案呗!
    30min过去了。。。咋统计啊= =

    好吧,反着失败了,我们试着正着来,正着需要7个值
    +至少在左边有
    +至少在中间有
    +至少在右边有
    -至少在左边和中间有
    -至少在左边和右边有
    -至少在中间和右边有
    +三个都有

    容斥原理嘛。。

    似乎可以维护了嘛

    设区间长为(l = R - L + 1)
    对于至少在左边有的
    我们需要找到结束位置最小的地方(t)(i >= t)的就是合法区间

    对于至少在右边有的
    我们需要找到结束位置最大的地方(t),(j <= t - l + 1)的就是合法区间

    对于至少在中间有的
    考虑从两个相邻的位置(b,a)(a >= b)
    然后他们产生的贡献是((a - b) * (b - l)),拆开就是((a - b) * b - l *(a - b))
    所以维护一个相邻位置的((a - b) * b)
    最后减去用区间最大值减最小值乘上(l)

    对于至少在左边和中间边有的
    (a)为出现最靠前的结束位置
    也是两个相邻位置(c,b)(b >= c)
    要求(c - l >= a)
    贡献就是((b - c) * (N - b))
    那就再维护一个((b - c) * b)好了

    至少在左边和右边有的
    (a)为最靠前的结束位置,(b)为最靠后的结束位置 - l + 1
    然后求左端点大于等于(a)
    右端点小于等于(b)的方案数

    至少在右边和中间有的
    (a)为最靠后的结束位置-l + 1
    相邻位置(c,b)(b >= c)
    要求是(b < a)
    然后统计起来是((b - c) * (c - l))
    维护((b - c) * c)事实上这是我们考虑只有中间有的时候维护的东西

    三个都有的
    (a)为最靠前的结束位置,(b)为最靠后的结束位置 - l + 1
    一个结束位置(c)必须(a + l <= c <= b - 1)
    然后相邻位置(c,d),有(c >= d)
    统计是((c - d) * (d - l - a + 1))也是和考虑只有中间有维护的东西一样

    维护的方式就是记录区间最大最小,合并左右区间的时候考虑中间两个点新的贡献

    后五种情况都要算一下边界的区间的方案

    然后就变成了,我们离线所有询问,把询问挂在节点上,然后dfs的时候合并线段树,就顺带处理了这个节点上所有询问的答案

    为啥我的代码又是别人的2倍????8.3K了解一下????
    实在是码农啊,服气服气,考场上给我五个点我也写不完,老了= =

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define MAXN 200005
    #define eps 1e-8
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N,Q;
    int64 ans[300005];
    char s[MAXN];
    vector<pii > R[MAXN],qry[MAXN];
    namespace SegmentTree {
        struct node {
    	int lc,rc;
    	int minv,maxv;
    	int64 sum[2];
        }tr[MAXN * 20];
        int rt[MAXN],Ncnt;
        void Init() {
    	tr[0].minv = N + 1,tr[0].maxv = 0,tr[0].sum[0] = tr[0].sum[1] = 0;
        }
        void update(int u) {
    	tr[u].minv = min(tr[tr[u].lc].minv,tr[tr[u].rc].minv);
    	tr[u].maxv = max(tr[tr[u].lc].maxv,tr[tr[u].rc].maxv);
    	for(int i = 0 ; i <= 1 ; ++i) tr[u].sum[i] = tr[tr[u].lc].sum[i] + tr[tr[u].rc].sum[i];
    	if(tr[u].lc && tr[u].rc) {
    	    int lm = tr[tr[u].lc].maxv,rm = tr[tr[u].rc].minv;
    	    if(lm <= rm) {
    		tr[u].sum[0] += 1LL * (rm - lm) * lm;
    		tr[u].sum[1] += 1LL * (rm - lm) * rm;
    	    }
    	}
    
        }
        int Merge(int u,int v,int L,int R) {
    	if(!u) return v;
    	if(!v) return u;
    	int mid = (L + R) >> 1;
    	tr[u].lc = Merge(tr[u].lc,tr[v].lc,L,mid);
    	tr[u].rc = Merge(tr[u].rc,tr[v].rc,mid + 1,R);
    	update(u);
    	return u;
        }
        void Insert(int &u,int p,int L,int R) {
    	if(!u) {u = ++Ncnt;}
    	if(L == R) {
    	    tr[u].minv = tr[u].maxv = p;tr[u].sum[0] = tr[u].sum[1] = 0;
    	    return;
    	}
    	int mid = (L + R) >> 1;
    	if(p <= mid) Insert(tr[u].lc,p,L,mid);
    	else Insert(tr[u].rc,p,mid + 1,R);
    	update(u);
        }
    
        int64 Query(int u,int ql,int qr,int L,int R,int id,int &v,pii &rg) {
    	if(!u) return 0;
    	if(L == ql && R == qr) {
    	    rg.fi = min(rg.fi,tr[u].minv);
    	    rg.se = max(rg.se,tr[u].maxv);
    	    int64 res = tr[u].sum[id];
    	    if(!id) {
    		if(v <= tr[u].minv) {
    		    res += 1LL * (tr[u].minv - v) * v;
    		}
    		if(tr[u].maxv <= N) v = tr[u].maxv;
    	    }
    	    else {
    		if(v >= tr[u].maxv) {
    		    res += 1LL * (v - tr[u].maxv) * v;
    		}
    		if(tr[u].minv >= 1) v = tr[u].minv;
    	    }
    	    return res;
    
    	}
    	int mid = (L + R) >> 1;
    	if(qr <= mid) return Query(tr[u].lc,ql,qr,L,mid,id,v,rg);
    	else if(ql > mid) return Query(tr[u].rc,ql,qr,mid + 1,R,id,v,rg);
    	else {
    	    if(!id) return Query(tr[u].lc,ql,mid,L,mid,id,v,rg) + Query(tr[u].rc,mid + 1,qr,mid + 1,R,id,v,rg);
    	    else return Query(tr[u].rc,mid + 1,qr,mid + 1,R,id,v,rg) + Query(tr[u].lc,ql,mid,L,mid,id,v,rg);
    	}
        }
    }
    using SegmentTree::rt;
    using SegmentTree::tr;
    using SegmentTree::Query;
    using SegmentTree::Insert;
    using SegmentTree::Merge;
    namespace SuffixTree {
        struct node {
    	int to,next,len;
        }E[MAXN * 4];
        int head[MAXN],sumE,ed[MAXN],dis[MAXN],fa[MAXN][20],Ncnt;
        void add(int u,int v,int c) {
    	E[++sumE].to = v;
    	E[sumE].next = head[u];
    	E[sumE].len = c;
    	head[u] = sumE;
        }
        void dfs(int u) {
    	for(int i = head[u] ; i ; i = E[i].next) {
    	    int v = E[i].to;
    	    dis[v] = dis[u] + E[i].len;
    	    fa[v][0] = u;
    	    dfs(v);
    	}
        }
        void pre_Process() {
    	dfs(1);
    	for(int j = 1 ; j <= 18 ; ++j) {
    	    for(int i = 1 ; i <= Ncnt ; ++i) {
    		fa[i][j] = fa[fa[i][j - 1]][j - 1];
    	    }
    	}
    	for(int i = 1 ; i <= Ncnt ; ++i) {
    	    if(ed[i]) {
    		for(auto t : R[ed[i]]) {
    		    int h = ed[i] - t.fi + 1;
    		    int u = i;
    		    for(int l = 18 ; l >= 0 ; --l) {
    			if(dis[fa[u][l]] >= h) u = fa[u][l];
    		    }
    		    qry[u].pb(mp(h,t.se));
    		}
    	    }
    	}
        }
        void Calc(int u) {
        	for(int i = head[u] ; i ; i = E[i].next) {
        	    int v = E[i].to;
        	    Calc(v);
        	    rt[u] = Merge(rt[u],rt[v],1,N);
        	}
        	if(ed[u]) Insert(rt[u],ed[u],1,N);
        	for(auto k : qry[u]) {
        	    int tmp,t;
        	    pii rg;
        	    int64 all = 0;
        	    //on the left
        	    if(tr[rt[u]].minv >= 1) {
    		t = tr[rt[u]].minv;
    		ans[k.se] += 1LL * (N - t) * (N - t - 1) / 2;
        	    }
        	    //on the right
        	    if(tr[rt[u]].maxv <= N) {
    		t = tr[rt[u]].maxv - k.fi + 1;
    		ans[k.se] += 1LL * (t - 1) * (t - 2) / 2;
        	    }
        	    //on the middle
        	    ans[k.se] += tr[rt[u]].sum[0];
        	    if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
    		ans[k.se] -= 1LL * (tr[rt[u]].maxv - tr[rt[u]].minv) * k.fi;
    		ans[k.se] += 1LL * (N - tr[rt[u]].maxv) * (tr[rt[u]].maxv - k.fi);
        	    }
    
        	    //on the left && middle
        	    all = 0;
        	    if(tr[rt[u]].minv <= N) {
    		rg = mp(N + 1,0);
    		int a = tr[rt[u]].minv;
    		if(a + k.fi <= N) {
    		    all -= Query(rt[u],a + k.fi,N,1,N,1,tmp = 0,rg);
    		    if(rg.fi <= rg.se) {
    			all += 1LL * N * (rg.se - rg.fi);
    			all += 1LL * (N - rg.fi) * (rg.fi - k.fi - a + 1);
    		    }
    		}
        	    }
        	    ans[k.se] -= all;
    
        	    //on the left && right
        	    if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
    		int a = tr[rt[u]].minv,b = tr[rt[u]].maxv - k.fi + 1;
    		if(a <= b) ans[k.se] -= 1LL * (b - a) * (b - a - 1) / 2;
        	    }
    
                //on the middle && right
        	    all = 0;
        	    if(tr[rt[u]].maxv >= 1) {
    		rg = mp(N + 1,0);
    		int a = tr[rt[u]].maxv - k.fi + 1;
    		if(a - 1 >= 1) {
    		    all += Query(rt[u],1,a - 1,1,N,0,tmp = N + 1,rg);
    		    if(rg.fi <= rg.se) {
    			all -= 1LL * k.fi * (rg.se - rg.fi);
    			all += 1LL * (a - rg.se) * (rg.se - k.fi);
    		    }
    		}
        	    }
        	    ans[k.se] -= all;
    
        	    //on the left && middle && right
        	    all = 0;
        	    if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
                	int a = tr[rt[u]].minv,b = tr[rt[u]].maxv - k.fi + 1;
                	rg = mp(N + 1,0);
                	if(a + k.fi <= b - 1) {
                	    all += Query(rt[u],a + k.fi,b - 1,1,N,0,tmp = N + 1,rg);
                	    if(rg.fi <= rg.se) {
    			all -= 1LL * (rg.se - rg.fi) * (k.fi + a - 1);
    			all += 1LL * (b - rg.se) * (rg.se - k.fi + 1 - a);
                	    }
                	}
        	    }
        	    ans[k.se] += all;
        	}
        }
        void Solve() {
    	Calc(1);
    	for(int i = 1 ; i <= Q ; ++i) {
    	    out(ans[i]);enter;
    	}
        }
    }
    using SuffixTree::ed;
    using SuffixTree::add;
    namespace SAM {
        struct node {
    	node *nxt[11],*per;
    	int len,cnt;
        }pool[MAXN],*tail = pool,*root,*last,*que[MAXN];
        int c[MAXN];
        void Init() {
    	root = last = tail++;
    	root->len = 0;root->cnt = 0;root->per = NULL;
        }
        void build_SAM(int c,int len) {
    	node *nowp = tail++,*p;
    	nowp->len = len;nowp->cnt = 1;
    	for(p = last ; p && !p->nxt[c] ; p = p->per) {
    	    p->nxt[c] = nowp;
    	}
    	if(!p) nowp->per = root;
    	else {
    	    node *q = p->nxt[c];
    	    if(q->len == p->len + 1) nowp->per = q;
    	    else {
    		node *copyq = tail++;
    		*copyq = *q;
    		copyq->cnt = 0;
    		copyq->len = p->len + 1;
    		q->per = nowp->per = copyq;
    		for( ; p && p->nxt[c] == q ; p = p->per) {
    		    p->nxt[c] = copyq;
    		}
    	    }
    	}
    	last = nowp;
        }
        void build_ST() {
    	int m = tail - pool;
    	for(int i = 0 ; i < m ; ++i) {
    	    c[pool[i].len]++;
    	}
    	for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
    	for(int i = 0 ; i < m ; ++i) {
    	    que[c[pool[i].len]--] = &pool[i];
    	}
    	for(int i = m ; i >= 1 ; --i) {
    	    if(que[i]->per) {
    		int u = que[i] - pool + 1,f = que[i]->per - pool + 1;
    		add(f,u,que[i]->len - que[i]->per->len);
    		if(que[i]->cnt) ed[u] = que[i]->len;
    	    }
    	}
    	SuffixTree::Ncnt = m;
        }
    }
    
    void Solve() {
        read(N);read(Q);
        scanf("%s",s + 1);
        SAM::Init();
        for(int i = 1 ; i <= N ; ++i) {
    	SAM::build_SAM(s[i] - '0',i);
        }
        SAM::build_ST();
        int l,r;
        for(int i = 1 ; i <= Q ; ++i) {
    	read(l);read(r);
    	R[r].pb(mp(l,i));
        }
        SegmentTree::Init();
        SuffixTree::pre_Process();
        SuffixTree::Solve();
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Solve();
    }
    
  • 相关阅读:
    php 元字符与转义
    php内置函数
    php系统常量
    WPF 批量修改控件属性
    对实体属性值赋值(DATASET转list)
    WPF 进度条实现
    WPF 异步刷新主界面
    c# 依赖注入Export ImportMany
    Oracle 正则匹配实现字符拆分
    Oracle 获取表对应列信息和索引信息
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9991366.html
Copyright © 2020-2023  润新知