zoukankan      html  css  js  c++  java
  • 【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();
    }
    
  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9991366.html
Copyright © 2011-2022 走看看