• 院赛K题:溯墨和斐波那契数列(莫队算法+线段树)


    题意:

    给出一个序列,每次询问给出一个区间,请你先把区间里的数排序并去重,然后计算出他们乘上斐波那契数列对应的项的和。

    题解:

    院赛的时候还不会莫队,交了学费。(虽然会莫队也大概率做不出)

    首先思考题目的性质,每次给出一个区间,把区间里的数排序并去重,然后计算答案,这暴力肯定是不行的,时间复杂度不可接受。

    整个题目没有修改,只有询问,考虑用莫队算法先优化。先用莫队把每个询问排序,然后移动指针,可以算出每个询问区间里面出现的数的信息,一开始我的做法是每次询问暴力求一遍区间内的值,还是TLE了,考虑进一步优化。

    我们建一颗权值线段树来做对序列元素的存储,线段树的节点表示这个节点维护的区间内的数乘他们对应的斐波那契数列项的和。那么问题就来了,每次插入新节点到线段树中的时候,这个节点之后的每一个答案都要发生变化,这样整棵树就废了。这里要用到一个斐波那契数列的性质(我不会),就是下面这样:

    f(n+m) = f(n)*f(m-1) + f(n+1)*f(m)

    根据这个公式,每次线段树区间合并的时候,右区间的每个数对应的斐波那契数列项要往后推x位,这个x是左区间的结点数,这是固定的,这样就可以用下面这个做法实现在log的时间复杂度内对线段树的维护。

    int k=segTree[i<<1].size;
        segTree[i].size=segTree[i<<1].size+segTree[i<<1|1].size;
        segTree[i].pre=segTree[i<<1].pre+segTree[i<<1|1].pre*(k>0?f[k-1]:1)+segTree[i<<1|1].now*f[k];
        segTree[i].pre%=mod;
        segTree[i].now=segTree[i<<1].now+segTree[i<<1|1].pre*f[k]+segTree[i<<1|1].now*f[k+1];
        segTree[i].now%=mod;

    完整代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e4+100;
    const int mod=1e9+7;
    typedef long long ll;
    int a[maxn],t[maxn];
    int cnt[maxn];
    int belong[maxn];
    int n,m,size,bnum,tt;
    ll now,ans[maxn],f[maxn];
    struct node {
        int l,r;
        ll now;
        ll pre;
        ll size;//记录左子树节点数 
    }segTree[maxn*4];
    void pushup (int i) {
        int k=segTree[i<<1].size;
        segTree[i].size=segTree[i<<1].size+segTree[i<<1|1].size;
        segTree[i].pre=segTree[i<<1].pre+segTree[i<<1|1].pre*(k>0?f[k-1]:1)+segTree[i<<1|1].now*f[k];
        segTree[i].pre%=mod;
        segTree[i].now=segTree[i<<1].now+segTree[i<<1|1].pre*f[k]+segTree[i<<1|1].now*f[k+1];
        segTree[i].now%=mod;
    }
    
    void build (int i,int l,int r) {
        segTree[i].l=l;
        segTree[i].r=r;
        segTree[i].now=0;
        segTree[i].pre=0;
        segTree[i].size=0;
        if (l==r) {
            return;
        }
        int mid=(l+r)>>1;
        build(i<<1,l,mid);
        build(i<<1|1,mid+1,r);
        //pushup(i);
    }
    void update (int i,int x,int b) {
        if (segTree[i].l==segTree[i].r) {
            segTree[i].now+=b*t[x];
            segTree[i].size+=b;
            return;
        }
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if (x<=mid)
            update(i<<1,x,b);
        if (x>mid)
            update(i<<1|1,x,b);
        pushup(i);
    }
    
    
    
    
    struct query {
        int l,r,id;
    }q[maxn];
    int cmp (query a,query b) {
        return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);
    } 
    void add(int pos) {
        if (!cnt[a[pos]]) {
            //说明插入新数
            update(1,a[pos],1);
        }
        ++cnt[a[pos]];
        
    }
    void del(int pos) {
        --cnt[a[pos]];
        if (!cnt[a[pos]]) {
            update(1,a[pos],-1);
        }
    }
    map<pair<int,int>,ll> mp;
    int main () { 
        scanf("%d",&n);
        f[1]=f[2]=1;
        for (int i=3;i<=maxn;i++) f[i]=f[i-1]+f[i-2],f[i]%=mod;    
        size=sqrt(n);
        bnum=n/size+(n%size>0);
        for (int i=1;i<=bnum;i++) 
            for (int j=(i-1)*size+1;j<=i*size;j++)
                belong[j]=i;
        for (int i=1;i<=n;i++) scanf("%d",&a[i]),t[i]=a[i];
        sort(t+1,t+n+1);
        tt=unique(t+1,t+n+1)-t-1;
        build(1,1,tt+2);
        //printf("%d
    ",tt);
        for (int i=1;i<=n;i++) a[i]=upper_bound(t+1,t+tt+1,a[i])-t-1;
        //for (int i=1;i<=n;i++) printf("%d
    ",a[i]);
        scanf("%d",&m);
        for (int i=1;i<=m;i++) {
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp);
        int l=1,r=0;
        for (int i=1;i<=m;i++) {
            int ql=q[i].l;
            int qr=q[i].r;
            if (mp[make_pair(ql,qr)]) {
                ans[q[i].id]=mp[make_pair(ql,qr)];
                continue;
            }
            while (l<ql) del(l++);
            while (l>ql) add(--l);
            while (r<qr) add(++r);
            while (r>qr) del(r--);
            
            ans[q[i].id]=segTree[1].now;
            mp[make_pair(ql,qr)]=segTree[1].now;
        }
        for (int i=1;i<=m;i++) cout<<ans[i]<<"
    "; 
    }
  • 相关阅读:
    基础知识漫谈(5):应用面向对象来分析“语言”
    【线段树】BZOJ2752: [HAOI2012]高速公路(road)
    【树状数组】BZOJ3132 上帝造题的七分钟
    【AC自动机】Lougu P3796
    【Splay】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)
    【fhq Treap】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)
    【可持久化线段树】POJ2104 查询区间第k小值
    【RMQ】洛谷P3379 RMQ求LCA
    【倍增】洛谷P3379 倍增求LCA
    【网络流】POJ1273 Drainage Ditches
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13262335.html
Copyright © 2020-2023  润新知