天天看點

男神zyh的青睐

​​ G 男神zyh的青睐 ​​

這道題與普通的莫隊不一樣的地方是,普通的莫隊是求一個\([l,r]\)區間裡面的貢獻,但是這道題需要求兩個區間貢獻之間的積。

普通的莫隊是二維的,對左端點進行分塊,然後再在塊内對右端點進行排序,複雜度為\(O(n^{\frac{3}{2}})\)。

但是本題的莫隊是三維的,對左端點進行分塊,然後再在塊内對右端點進行分塊,然後再在塊内對時間 t 進行排序,可以證明當塊的大小取\(n^{\frac{2}{3}}\)的時候,算法的總複雜度為\(O(n^{\frac{5}{3})}\)

可以利用容斥原理,将原本是四維的莫隊,降為三維,把\((r_1-l_1)\times(r_2-l_2)\)變為\((r_2-l_2)\times[1,r]-(r_2-l_2)\times[1,l_1-1]\)

不過我也嘗試了一下四維莫隊的解法,複雜度為\(O(n^{\frac{7}{4}})\),大概是1e8,在經過億點點優化之後,居然卡過去了。

然後又學會了二維莫隊的寫法

二維莫隊也是通過容斥進行降維的

如果用\(c(l,r)\)表示一段區間的貢獻,那麼我們的總貢獻就是\(c_1(l_1,r_1)\times c_2(l_2,r_2)\),然後可以發現\(c()\)是線性變化的,是以可以利用容斥進行拆分,\(f(a)\)表示\(c(1,a)\),\(c_1(l_1,r_1)\times c_2(l_2,r_2)=[f_1(r_1)-f_1(l_1-1)]\times [f_2(r_2)-f_2(l_2-1)]\)

\(=f_1(r_1)\times f_2(r_2)+f_1(l_1-1)\times f_2(l_2-1)-f_1(r_1)\times f_2(l_2-1)-f_2(r_2)\times f_1(l_1-1)\)

這樣,利用容斥,我們就成功地把這個莫隊降成了二維的莫隊,其實三維的莫隊也是這樣的降的....

二維寫法:

// Created by CAD
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn=5e4+5;
const int mod=1e9+7;
int a[maxn],cnt[2][maxn],belo[maxn];
struct query{
    int l,r,f,id;
    bool operator <(const query& q)const {
        return belo[l]!=belo[q.l]?(belo[l]<belo[q.l]):((belo[l]&1)?r<q.r:r>q.r);
    }
}q[maxn<<2];
ll now;
int ans[maxn],d[maxn];
inline void add(int &x,int i){
    (now+=cnt[i^1][x])%=mod;
    ++cnt[i][x];
}
inline void del(int &x,int i){
    (now-=cnt[i^1][x])%=mod;
    --cnt[i][x];
}
inline ll qpow(ll x,ll n){
    ll ans=1;
    while(n>0){
        if(n&1)
            ans=ans*x%mod;
        x=x*x%mod,n>>=1;
    }
    return ans;
}

int main() {
    int n,m;scanf("%d%d",&n,&m);
    int blo=pow(n,0.5);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        belo[i]=(i-1)/blo-1;
    }
    for(int i=1;i<=m;++i){
        int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        q[4*i-3]={r1,r2,1,i};
        q[4*i-2]={r1,l2-1,-1,i};
        q[4*i-1]={l1-1,r2,-1,i};
        q[4*i]={l1-1,l2-1,1,i};
        d[i]=1ll*(r2-l2+1)*(r1-l1+1)%mod;
    }
    sort(q+1,q+4*m+1);
    int l=0,r=0;
    for(int i=1;i<=4*m;++i){
        int ql=q[i].l,qr=q[i].r,qf=q[i].f;
        while(l<ql) add(a[++l],0);
        while(l>ql) del(a[l--],0);
        while(r<qr) add(a[++r],1);
        while(r>qr) del(a[r--],1);

        (ans[q[i].id]+=now*qf)%=mod;
    }
    for(int i=1;i<=m;++i)
        printf("%lld\n",1ll*(ans[i]+mod)%mod*qpow(d[i],mod-2)%mod);
    return 0;
}      

三維寫法:

// Created by CAD
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn=5e4+5;
const int mod=1e9+7;
int belo[maxn];
struct query{
    int l,r,id,t;
    bool operator<(const query &q)const {
        return belo[l]!=belo[q.l]?(belo[l]<belo[q.l]):(belo[r]!=belo[q.r]?(belo[r]<belo[q.r]):(t<q.t));
    }
}q[maxn<<1];
int a[maxn],cnt1[maxn],cnt2[maxn],ans[maxn];
ll now;
ll d[maxn];
inline void add(int x){
    now+=cnt1[a[x]];
    now%=mod;
    cnt2[a[x]]++;
}
inline void del(int x){
    now-=cnt1[a[x]];
    now%=mod;
    cnt2[a[x]]--;
}
inline void ad(int x){
    now+=cnt2[a[x]];
    now%=mod;
    cnt1[a[x]]++;
}
inline void de(int x){
    now-=cnt2[a[x]];
    now%=mod;
    cnt1[a[x]]--;
}

ll qpow(ll x,ll n){
    ll ans=1;
    while(n>0){
        if(n&1) ans=ans*x%mod;
        x=x*x%mod,n>>=1;
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;cin>>n>>m;
    int blo=pow(n,2/3.0);
    for(int i=1;i<=n;++i){
        cin>>a[i];
        belo[i]=(i-1)/blo+1;
    }
    for(int i=1;i<=m;++i){
        int l1,r1,l2,r2;cin>>l1>>r1>>l2>>r2;
        q[i*2-1]={l2,r2,-i,l1-1};
        q[i*2]={l2,r2,i,r1};
        d[i]=1ll*(r2-l2+1)*(r1-l1+1)%mod;
    }
    sort(q+1,q+2*m+1);
    int l=1,r=0,t=0;
    for(int i=1;i<=2*m;++i){
        int ql=q[i].l,qr=q[i].r,qt=q[i].t;
        while(l<ql) del(l++);
        while(l>ql) add(--l);
        while(r<qr) add(++r);
        while(r>qr) del(r--);

        while(t<qt) ad(++t);
        while(t>qt) de(t--);

        ans[abs(q[i].id)]+=q[i].id/abs(q[i].id)*now;
        ans[abs(q[i].id)]%=mod;
    }
    for(int i=1;i<=m;++i){
        cout<<1ll*(ans[i]+mod)%mod*qpow(d[i],mod-2)%mod<<endl;
    }
    return 0;
}      

四維寫法:

// Created by CAD
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn=5e4+5;
const int mod=1e9+7;
int belo[maxn];
struct query{
    int p[4],id;
    bool operator<(const query &q)const {
        for(int i=0;i<3;++i)
            if(belo[p[i]]!=belo[q.p[i]]){
                if(!i||belo[p[i-1]]&1)
                    return belo[p[i]]<belo[q.p[i]];
                else
                    return belo[p[i]]>belo[q.p[i]];
            }

        if(belo[p[2]]&1)
            return p[3]<q.p[3];
        else return p[3]>q.p[3];
    }
}q[maxn<<1];
int a[maxn],cnt[2][maxn],ans[maxn];
ll now;
ll d[maxn];
inline void add(int &x,int &i){
    now+=cnt[i^1][x];
    now%=mod;
    ++cnt[i][x];
}
inline void del(int &x,int &i){
    now-=cnt[i^1][x];
    now%=mod;
    --cnt[i][x];
}

inline ll qpow(ll x,ll n){
    ll ans=1;
    while(n>0){
        if(n&1) ans=ans*x%mod;
        x=x*x%mod,n>>=1;
    }
    return ans;
}

int main() {
    int n,m;scanf("%d%d",&n,&m);
    int blo=pow(n,0.74);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        belo[i]=(i-1)/blo+1;
    }
    for(int i=1;i<=m;++i){
        int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        q[i]={l1,r1,l2,r2,i};
        d[i]=1ll*(r2-l2+1)*(r1-l1+1)%mod;
    }
    sort(q+1,q+m+1);
    int p[4]={1,0,0,0};
    for(int i=1;i<=m;++i){
        for(int j=0;j<2;++j){
            int &l=p[j*2],&r=p[j*2+1];
            int &ql=q[i].p[j*2],&qr=q[i].p[j*2+1];
            while(l<ql) del(a[l++],j);
            while(l>ql) add(a[--l],j);
            while(r<qr) add(a[++r],j);
            while(r>qr) del(a[r--],j);
        }
        ans[q[i].id]=now;
    }
    for(int i=1;i<=m;++i)
        printf("%lld\n",1ll*(ans[i]+mod)%mod*qpow(d[i],mod-2)%mod);
    return 0;
}