1. 程式人生 > >【LOJ】#2479. 「九省聯考 2018」制胡竄

【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('\n')
#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();
}