CF gym 100962D Deep Purple [后缀树,树链剖分,线段树]

Codeforces

思路

感觉这个离线的思路好神仙啊qwq

对于每个询问\([l,r]\)其实就是要求\(p_{max}\),使得\(lcs(s[1,p],s[1,r])>p-l\),也就是\(lcs(s[1,p],s[1,r])+l>p\)。

首先把询问离线按\(r\)排序,然后从右往左扫,每次

  1. 处理之前已经被加进去的询问,看当前位置是否能被作为\(p\),然后把已经处理完毕的询问给删掉。
  2. 把当前询问塞进去。

建出反串的后缀树,那么不等式左边就是\(dep_{lca(p,r)}+l\)。

暴力的做法就是枚举\(lca\),然后处理询问的时候查一下这个位置的\(l\),塞询问的时候把\(l\)塞进去。

用树链剖分加速:对于每一条链把\(tag\)打在深度最深的地方。处理询问的时候就查一下\(p\)到根上的\(\max(dep_x+l)\)以及这个\(tag\)来自哪个询问,然后将它删掉。塞询问的时候就一路往上跳,打\(tag\)。

怎么查询?分最大的\(tag\)是在上面还是下面来讨论,如果是上面就直接查,否则查一下\(\max(l)\)再加上当前的\(dep\)。

由于每个点只能有一个最大的\(tag\)放在线段树上,所以还要用一个\(set\)记录这个点有哪些标记。

(可能讲得不是很清楚qwq)

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 402020
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char __sr[1<<21],__z[20];int __C=-1,__zz=0;
    inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
    inline void print(register int x)
    {
        if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
        while(__z[++__zz]=x%10+48,x/=10);
        while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
    }
    void file()
    {
        #ifdef NTFOrz
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,Q;
char s[sz];
int pos[sz];
struct hh{int l,r,id;const bool operator < (const hh &x) const {return r>x.r;}}q[sz];
int ans[sz];

int TT;
//struct SegmentTree
//{
    pii tr[sz<<2][2];
    #define ls k<<1
    #define rs k<<1|1
    #define lson ls,l,mid
    #define rson rs,mid+1,r
    void pushup(int k){tr[k][TT]=max(tr[ls][TT],tr[rs][TT]);}
    void modify(int k,int l,int r,int x,pii w)
    {
        if (l==r) return (void)(tr[k][TT]=w);
        int mid=(l+r)>>1;
        if (x<=mid) modify(lson,x,w);
        else modify(rson,x,w);
        pushup(k);
    }
    pii query(int k,int l,int r,int x,int y)
    {
        if (x<=l&&r<=y) return tr[k][TT];
        int mid=(l+r)>>1;pii ret=MP(-1e9,0);
        if (x<=mid) chkmax(ret,query(lson,x,y));
        if (y>mid) chkmax(ret,query(rson,x,y));
        return ret;
    }
//}T1,T2;
// 1:维护一段里面最大的值是多少,以及它的位置。
// 2:维护一段里面加的l的最大值是多少,以及它的位置。 

namespace Tree
{
    struct hh{int t,nxt;}edge[sz];
    int head[sz],ecnt;
    void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}
    int dfn[sz],top[sz],bot[sz],size[sz],son[sz],fa[sz],len[sz],T;
    #define v edge[i].t
    void dfs1(int x)
    {
        size[x]=1;
        go(x)
        {
            fa[v]=x;dfs1(v);
            size[x]+=size[v];
            if (size[v]>size[son[x]]) son[x]=v;
        }
    }
    void dfs2(int x,int tp)
    {
        dfn[x]=++T;
        top[x]=tp,bot[tp]=x;
        if (son[x]) dfs2(son[x],tp);
        go(x) if (v!=son[x]) dfs2(v,v);
    }
    #undef v
    set<pii>s[sz];
    // 维护这个地方打的标记有哪些
    pii query(int x)
    {
        pii ret=MP(0,0);
        while (x)
        {
            TT=0;pii w1=::query(1,1,T,dfn[top[x]],dfn[x]);
            TT=1;pii w2=::query(1,1,T,dfn[x],dfn[bot[top[x]]]);
            w2.fir+=len[x];
            chkmax(ret,w1),chkmax(ret,w2);
            x=fa[top[x]];
        }
        return ret;
    }
    void modify(int x,int w,int id,bool add)
    {
        while (x)
        {
            if (add) s[x].insert(MP(w,id));
            else s[x].erase(MP(w,id));
            if (s[x].size())
            {
                auto cur=*s[x].rbegin();
                TT=1;::modify(1,1,T,dfn[x],cur);
                cur.fir+=len[x];
                TT=0;::modify(1,1,T,dfn[x],cur);
            }
            else TT=0,::modify(1,1,T,dfn[x],MP(0,0)),TT=1,::modify(1,1,T,dfn[x],MP(0,0));
            x=fa[top[x]];
        }
    }
}

namespace SAM
{
    struct hh{int link,len,ch[28];}a[sz];
    int lst=1,cnt=1,root=1;
    int add(int c)
    {
        int cur=++cnt,p=lst;lst=cur;a[cur].len=a[p].len+1;
        while (p&&!a[p].ch[c]) a[p].ch[c]=cur,p=a[p].link;
        if (!p) return a[cur].link=1,cur;
        int q=a[p].ch[c];
        if (a[q].len==a[p].len+1) return a[cur].link=q,cur;
        int t=++cnt;a[t]=a[q];a[q].link=a[cur].link=t;a[t].len=a[p].len+1;
        while (p&&a[p].ch[c]==q) a[p].ch[c]=t,p=a[p].link;
        return cur;
    }
    void build(){rep(i,2,cnt) Tree::make_edge(a[i].link,i),Tree::len[i]=a[i].len;}
}

int main()
{
    file();
    read(n,Q);
    cin>>(s+1);
    rep(i,1,n) pos[i]=SAM::add(s[i]-'a'+1);
    SAM::build();
    Tree::dfs1(1),Tree::dfs2(1,1);
    rep(i,1,Q) read(q[i].l,q[i].r),q[i].id=i;
    sort(q+1,q+Q+1);
    int p=1;
    drep(i,n,1)
    {
        while (233)
        {
            pii w=Tree::query(pos[i]);int id=w.sec;
            if (w.fir<=i) break;
            ans[q[id].id]=i-q[id].l+1;
            Tree::modify(pos[q[id].r],q[id].l,id,0);
        }
        while (p<=Q&&q[p].r==i) Tree::modify(pos[q[p].r],q[p].l,p,1),++p;
    }
    rep(i,1,Q) printf("%d\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/p-b-p-b/p/11318629.html

时间: 2024-08-29 19:24:46

CF gym 100962D Deep Purple [后缀树,树链剖分,线段树]的相关文章

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj3589】动态树 树链剖分+线段树

题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u,

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

【bzoj1959】[Ahoi2005]LANE 航线规划 离线处理+树链剖分+线段树

题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一些先遣飞船已经出发,在星球之间开辟探险航线. 探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线. 例如下图所示: 在5个星球之间,有5条探险航

【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1". 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n

HDU-3966 Aragorn&#39;s Story(树链剖分+线段树)

Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 12479    Accepted Submission(s): 3331 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lor

BZOJ2243 [SDOI2011]染色(树链剖分+线段树合并)

题目链接 BZOJ2243 树链剖分+线段树合并 线段树合并的一些细节需要注意一下 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 100010; int n, m,

【bzoj2238】Mst 最小生成树+树链剖分+线段树

题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两个正整数N,M(N<=50000,M<=100000)表示原图的顶点数和边数. 下面M行,每行三个整数X,Y,W描述了图的一条边(X,Y),其边权为W(W<=10000).保证两点之间至多只有一条边. 接着一行一个正整数Q,表示询问数.(1<=Q<=100000) 下面Q行,每行