院赛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]<<"
"; 
}
原文地址:https://www.cnblogs.com/zhanglichen/p/13262335.html