zoukankan      html  css  js  c++  java
  • hdu-6230 主席树/树状数组+离线

     主席树做法

      刚开始是听说了要用manacher采取写的,但是不难看出要使用manacher,因为题目要求的1个半回文串,其实就是两个回文中心的交集,你要找两个回文中心,让它们的回文半径可以互相覆盖即可。问题转化成了对于一个回文中心,它覆盖的左侧的所有回文中心中( 向右覆盖的范围超过i )这样的点的数量。这样就变成了求区间中大于x的数的个数,可以用主席树解决。

    #include<iostream>
    #include<cstring>
    #include <string>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+20;
    const int maxm=2e7+20;
    char s[maxn],s_new[maxn];
    int p[maxn];
    int init()
    {
        int len=(int)strlen(s);
        s_new[0]='$';
        s_new[1]='#';
        int j=2;
        for(int i=0;i<len;i++)
        {
            s_new[j++]=s[i];
            s_new[j++]='#';
        }
        s_new[j]=0;
        return j;
    }
    int manacher()
    {
        int len=init();
        int id=0,mx=0;
        for(int i=1;i<len;i++)
        {
            if(i<mx)
                p[i]=min(p[2*id-i],mx-i);
            else
                p[i]=1;
            while(s_new[i-p[i]]==s_new[i+p[i]])
                p[i]++;
            if(mx<i+p[i])
            {
                id=i;
                mx=i+p[i];
            }
        }
        return len;
    }
    int n,m,cnt;
    int tree[maxn],ls[maxm],rs[maxm],sum[maxm];
    void update(int k,int pre,int &now,int l,int r){
        now=++cnt;
        ls[now]=ls[pre];
        rs[now]=rs[pre];
        sum[now]=sum[pre]+1;
        
        if(l==r){
            return;
        }
        int m=(l+r)>>1;
        if(k<=m) update(k,ls[pre],ls[now],l,m);
        else update(k,rs[pre],rs[now],m+1,r);
    }
    int query(int a,int b,int l,int r,int L,int R){
        if(L<=l&&r<=R){
            return sum[b]-sum[a];
        }
        int ans=0;
        int mid=(l+r)>>1;
        if(mid>=L)
            ans+=query(ls[a], ls[b], l, mid, L, R);
        if(mid<R)
            ans+=query(rs[a], rs[b], mid+1, r, L, R);
        return ans;
    }
    int main()
    {
        int T;
        cin>>T;
        while(T--){
            cnt=0;
            tree[0]=ls[0]=rs[0]=sum[0]=0;
            scanf("%s",s);
            int len=manacher();
            m=len/2;
            ll ans=0;
            for(int i=1;i<m;i++)
            {
                int L=i-p[i*2]/2+1;
                if(L!=i)
                    update(L,tree[i-1],tree[i],1,m);
                else
                    tree[i]=tree[i-1];
            }
            
            for(int i=1;i<m;i++){
                int R=i+p[i*2]/2-1;
                int t=query(tree[i], tree[R], 1, m, 1, i);
                ans+=t;
            }
            
            printf("%lld
    ",ans);
        }
    }

      但是我的板子一直过不了,一直TLE,借用了以为其他人的主席树板子才能过,好迷啊。

      借用模版:https://blog.csdn.net/know_heng/article/details/78590061

    树状数组离线做法

      我觉得是主席树好想一点,因为很容易就可以转化到主席树经典模型上,这个离线树状数组好处就是非常的短,但是不容易想。

      考虑你要求的东西,在一个区间中小于x的数的个数,也就是说,你大于x的数字都没有作用对这个查询,所以我们在考虑这个查询的时候,把小于x的加入就行了。所以可以把所有离线查询按x排序,逐步按从小到大的顺序加入所有元素,并按前缀和计算答案就行了。

    #include<iostream>
    #include<cstring>
    #include <string>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+20;
    char s[maxn],s_new[maxn];
    int p[maxn];
    int init()
    {
        int len=(int)strlen(s);
        s_new[0]='$';
        s_new[1]='#';
        int j=2;
        for(int i=0;i<len;i++)
        {
            s_new[j++]=s[i];
            s_new[j++]='#';
        }
        s_new[j]=0;
        return j;
    }
    int manacher()
    {
        int len=init();
        int id=0,mx=0;
        for(int i=1;i<len;i++)
        {
            if(i<mx)
                p[i]=min(p[2*id-i],mx-i);
            else
                p[i]=1;
            while(s_new[i-p[i]]==s_new[i+p[i]])
                p[i]++;
            if(mx<i+p[i])
            {
                id=i;
                mx=i+p[i];
            }
        }
        return len;
    }
    int n;
    int a[maxn],c[maxn]; //对应原数组和树状数组
    
    int lowbit(int x){
        return x&(-x);
    }
    
    void updata(int i,int k){    //在i位置加上k
        while(i <= n){
            c[i] += k;
            i += lowbit(i);
        }
    }
    int getsum(int i){        //求A[1 - i]的和
        int res = 0;
        while(i > 0){
            res += c[i];
            i -= lowbit(i);
        }
        return res;
    }
    typedef pair<int, int> P;
    P t[maxn];
    int main()
    {
        int T;
        cin>>T;
        while(T--){
            
            int m;
            
            scanf("%s",s);
            int len=manacher();
            m=len/2;
            n=m;
            ll ans=0;
            for(int i=1;i<m;i++)
                t[i]=make_pair(i-p[i*2]/2+1, i);
            sort(t+1, t+m);
            for(int i=0;i<m;i++)
                c[i]=0;
            int cnt=1;
            for(int i=1;i<m;i++)
            {
                while(cnt<m&&t[cnt].first<=i){
    //                cout<<t[cnt].second<<endl;
                    updata(t[cnt].second, 1);
                    cnt++;
                }
                int R=i+p[i*2]/2-1;
    //            cout<<R<<" "<<getsum(R)<<endl;
                ans+=getsum(R)-getsum(i);
            }
            printf("%lld
    ",ans);
        }
    }

      

  • 相关阅读:
    数据库连接(1)-从JDBC到MyBatis
    基于 abp vNext 和 .NET Core 开发博客项目
    基于 abp vNext 和 .NET Core 开发博客项目
    正则表达式位置匹配
    正则表达式字符匹配
    2019年终总结
    Win10 1903 运行安卓模拟器蓝屏解决方案
    我已经看到了,撤回也没用了(PC微信防撤回补丁)
    DOCKER 学习笔记1 认识docker
    Java 中级 学习笔记 2 JVM GC 垃圾回收与算法
  • 原文地址:https://www.cnblogs.com/King-of-Dark/p/11603120.html
Copyright © 2011-2022 走看看