zoukankan      html  css  js  c++  java
  • BNUOJ 52317 As Easy As Possible 树上倍增/主席树

    题目链接:

    https://acm.bnu.edu.cn/v3/problem_show.php?pid=52317

    As Easy As Possible

    Case Time Limit: 1000ms
    Memory Limit: 524288KB
    ## 题意 > 给你一个只含'e','a','s','y'的字符串,问你区间[l,r]内像"easyeasyeasy"这样的最多"easy"重复的子序列,输出easy重复的次数。

    题解

    1、用倍增的思路来做,每个点只记录最靠近它的在它左边的那个字母的位置,比如'e'记录前面的'y','a'记录前面的'e','s'记录前面的'a'...。这样相当于得到了一些树形结构,用树上倍增的思路去做就可以了。
    具体的看代码比较好理解。

    其实主要的思路就是化整为0,对每个字母只考虑排在它前面的且最靠近它的在它左边的那个字母的位置。然后你发现这个就可以用树上倍增来快速求我们需要的子序列了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    const int maxn=101010;
    const int maxm=22;
    
    char str[maxn];
    int arr[maxn],p3[maxn];
    int n,m;
    int anc[maxn][maxm];
    int mp[4];
    
    int main(){
        scanf("%s",str+1);
        n=strlen(str+1);
        for(int i=1;i<=n;i++){
            if(str[i]=='e') arr[i]=0;
            else if(str[i]=='a') arr[i]=1;
            else if(str[i]=='s') arr[i]=2;
            else if(str[i]=='y') arr[i]=3;
        }
    
        ///mp[i]存数字i最后出现的位置
        memset(mp,0,sizeof(mp));
        for(int i=1;i<=n;i++){
            int pre=(arr[i]-1+4)%4;
            anc[i][0]=mp[pre];
            mp[arr[i]]=i;
            p3[i]=mp[3];
        }
    
        memset(anc[0],0,sizeof(anc[0]));
    
        for(int j=1;j<maxm;j++){
            for(int i=1;i<=n;i++){
                anc[i][j]=anc[anc[i][j-1]][j-1];
            }
        }
    
        scanf("%d",&m);
        while(m--){
            int l,r;
            scanf("%d%d",&l,&r);
            int v=p3[r];
            int res=1;
            for(int j=maxm-1;j>=0;j--){
                if(anc[v][j]>=l){
                    v=anc[v][j];
                    res+=1<<j;
                }
            }
            printf("%d
    ",res/4);
        }
    
        return 0;
    }
    

    2、主席树:
    从前晚后建树,如果单前不是'y',那么rt[i]=rt[i-1],如果是的话,就贪心匹配,找到左边最接近的"eas",rt[i]=rt['e'前一个位置-1],然后把e的位置插到rt[i]这颗线段树中。
    对于查询[l,r],直接去rt[r]中查找区间(l,r)的和就可以了。
    相当于每颗线段树rt[i]维护了以i为结尾的区间的往左边贪心的最长"easyeasyeasy..."的子串。

    #include<map>
    #include<set>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<ctime>
    #include<vector>
    #include<cstdio>
    #include<string>
    #include<bitset>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<functional>
    using namespace std;
    #define X first
    #define Y second
    #define mkp make_pair
    #define lson(i) (tre[(i)].ls)
    #define rson(i) (tre[(i)].rs)
    #define sumv(i) (tre[(i)].sum)
    #define mid (l+(r-l)/2)
    #define sz() size()
    #define pb(v) push_back(v)
    #define all(o) (o).begin(),(o).end()
    #define clr(a,v) memset(a,v,sizeof(a))
    #define bug(a) cout<<#a<<" = "<<a<<endl
    #define rep(i,a,b) for(int i=a;i<(b);i++)
    #define scf scanf
    #define prf printf
    
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<pair<int,int> > VPII;
    
    const int INF=0x3f3f3f3f;
    const LL INFL=0x3f3f3f3f3f3f3f3fLL;
    const double eps=1e-8;
    const double PI = acos(-1.0);
    
    //start----------------------------------------------------------------------
    
    const int maxn=101010;
    
    ///nlogn空间复杂度
    struct Tre {
        int ls,rs,sum;
        Tre() {
            ls=rs=sum=0;
        }
    } tre[maxn*20];
    
    int rt[maxn],tot;
    
    int _pos;
    void update(int &o,int l,int r) {
        tre[++tot]=tre[o],o=tot;
        if(l==r) {
            sumv(o)++;
        } else {
            if(_pos<=mid) update(lson(o),l,mid);
            else update(rson(o),mid+1,r);
            sumv(o)=sumv(lson(o))+sumv(rson(o));
        }
    }
    
    int _res,ql,qr;
    void query(int o,int l,int r){
        if(ql<=l&&r<=qr){
            _res+=sumv(o);
        }else{
            if(ql<=mid) query(lson(o),l,mid);
            if(qr>mid) query(rson(o),mid+1,r);
        }
    }
    
    char str[maxn];
    int n,m;
    
    void init(){
        rt[0]=tot=0;
    }
    
    int solve(int pos){
        int pre=0;
        while(pos>0){
            if(str[pos]=='s'&&pre==0) pre=1;
            else if(str[pos]=='a'&&pre==1) pre=2;
            else if(str[pos]=='e'&&pre==2) pre=3;
            if(pre==3) break;
            pos--;
        }
        return pos;
    }
    
    int main() {
        scf("%s",str+1);
        n=strlen(str+1);
        init();
        for(int i=1;i<=n;i++){
    
            if(str[i]=='y'){
                _pos=solve(i-1);
                if(_pos>0){
                    rt[i]=rt[_pos-1];
                    update(rt[i],1,n);
                }else{
                    rt[i]=rt[i-1];
                }
            }else{
                rt[i]=rt[i-1];
            }
        }
    
        scf("%d",&m);
        while(m--){
            int l,r;
            scf("%d%d",&l,&r);
            ql=l,qr=r,_res=0;
            query(rt[r],1,n);
            prf("%d
    ",_res);
        }
    
        return 0;
    }
    
    //end-----------------------------------------------------------------------
  • 相关阅读:
    DataTable转Json就是这么简单(Json.Net DLL (Newtonsoft))
    Mysql查询数据库 整理
    Mysq基础l数据库管理、表管理、增删改数据整理
    zTree基础
    ECharts基础
    layui基础总结
    Bootstrap基础
    JQuery进阶
    JQuery基础总结
    Javascript鼠标键盘事件
  • 原文地址:https://www.cnblogs.com/fenice/p/5933347.html
Copyright © 2011-2022 走看看