莫隊算法

莫隊算法詳解
DQUERY - D-query
題意:
求區(qū)間內(nèi)不同元素的數(shù)量宅倒,也就是求出現(xiàn)次數(shù)>=1的元素個數(shù)

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=30010;
const int MAXQ=200010;
const int BOUND=1e6+10;
int pos[MAXN];
int num[MAXN];
int ans[MAXQ];
int cnt[BOUND];
int res;
struct Query
{
    int lef,rig,id;
    bool operator <(const Query &t) const
    {
        if(pos[lef]==pos[t.lef]) return rig<t.rig;
        return pos[lef]<pos[t.lef];
    }
};
void add(int position)
{
    cnt[num[position]]++;
    if(cnt[num[position]]==1) res++;
}
void dele(int position)
{
    cnt[num[position]]--;
    if(cnt[num[position]]==0) res--;
}
Query query[MAXQ];
int main()
{
    int n,m,blocks;
    scanf("%d",&n);
    blocks=ceil(sqrt(n));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",num+i);
        pos[i]=(i-1)/blocks;
    }
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&query[i].lef,&query[i].rig);
        query[i].id=i;
    }
    sort(query,query+m);
    memset(cnt,0,sizeof(cnt));
    int currL=1,currR=0;//currL為1,currR為0,是為了剛開始移動時不會加到非法的區(qū)間
    res=0;
    for(int i=0;i<m;i++)
    {// 由于初始區(qū)間 l >r ,所以下面循環(huán)得從r 開始扼睬,
    //如果查詢區(qū)間不是從1開始就會出現(xiàn)l經(jīng)過一段,r重復經(jīng)過這一段跨释。
        while(currR<query[i].rig) {currR++; add(currR);  }
        while(currR>query[i].rig) {dele(currR);currR--;}
        while(currL<query[i].lef) { dele(currL);currL++; }
        while(currL>query[i].lef) { add(currL-1);currL--; }
        ans[query[i].id]=res;
    }
    for(int i=0;i<m;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}

小Z的襪子(hose)
題意:
在區(qū)間內(nèi)選出一對相同顏色的襪子的概率
題解:
假設區(qū)間[L,R]有y種顏色的襪子,分別為a,b,c...x,那么從[L,R]任選兩個襪子的組合數(shù)為C(R-L+1,2)=(R-L+1)*(R-L)/2;選出為a顏色的襪子組合數(shù)為C(a,2)=a*(a-1)/2,...選出為x顏色的襪子組合數(shù)為C(x,2)=x*(x-1)/2,
所以概率為(C(a,2)+C(b,2)...+C(x,2))/(C(R-L+1,2))
=( a*(a-1)+b*(b-1)+...+x*(x-1) )/ ( (R-L+1)*(R-L) )
=(a*a+b*b+...+x*x-(a+b+c...+x) )/ ( (R-L+1)*(R-L) )
而(a+b+c...+x)=(R-L+1)
所以概率為(a*a+b*b+...+x*x-(R-L+1) )/ ( (R-L+1)*(R-L) )

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=50010;
long long cnt[MAXN];
int pos[MAXN];
long long num[MAXN];
long long ans1[MAXN];
long long ans2[MAXN];
long long sum,gg;
struct Query
{
    int lef,rig,id;
    bool operator<(const Query &l) const
    {
        if(pos[lef]==pos[l.lef]) return rig<l.rig;
        return pos[lef]<pos[l.lef];
    }
};
Query query[MAXN];
void add(int position)
{
    sum-=cnt[num[position]]*cnt[num[position]];
    cnt[num[position]]++;
    sum+=cnt[num[position]]*cnt[num[position]];
}
void del(int position)
{
    sum-=cnt[num[position]]*cnt[num[position]];
    cnt[num[position]]--;
    sum+=cnt[num[position]]*cnt[num[position]];
}
long long gcd(long long a,long long b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int n,m,blocks;
    scanf("%d%d",&n,&m);
    blocks=ceil(sqrt(n));
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",num+i);
        pos[i]=(i-1)/blocks;
    }
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&query[i].lef,&query[i].rig);
        query[i].id=i;
    }
    sort(query,query+m);
    memset(cnt,0,sizeof(cnt));
    int currL=1,currR=0,L,R;
    sum=0;
    for(int i=0;i<m;i++)
    {
        L=query[i].lef;R=query[i].rig;
        while(currR>R) {del(currR);currR--;}
        while(currR<R) { currR++; add(currR);}
        while(currL<L){ del(currL);currL++;}
        while(currL>L) {add(currL-1);currL--;}
        long long up=sum-(long long)(query[i].rig-query[i].lef+1);
        long long down=(long long)(query[i].rig-query[i].lef+1)*(long long)(query[i].rig-query[i].lef);
        gg=gcd(up,down);
        ans1[query[i].id]=up/gg;
        ans2[query[i].id]=down/gg;
    }
    for(int i=0;i<m;i++)
    {
        printf("%lld/%lld\n",ans1[i],ans2[i]);
    }
    return 0;
}

類似題目:
G - Sona :求區(qū)間相同數(shù)字個數(shù)的立方和

Mato的文件管理
題意:
給定若干個區(qū)間厌处,求出逆序數(shù)
題解:
用數(shù)狀數(shù)組維護逆序數(shù)的變化

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
const int MAXN=50010;
/*數(shù)狀數(shù)組*/
int c[MAXN],n;
int lowbit(int x){ return x&(-x);}
void add(int x,int val)
{
    while(x<=n)
    {
        c[x]+=val;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int res=0;
    while(x>0)
    {
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}
/*莫隊算法*/
int cpy[MAXN],num[MAXN],pos[MAXN],ans[MAXN],currSum;
struct Query
{
    int lef,rig,id;
    bool operator<(const Query& t) const
    {
        if(pos[lef]==pos[t.lef]) return rig<t.rig;
        return pos[lef]<pos[t.lef];
    }
};
Query query[MAXN];
int main()
{
    int m,blocks;
    scanf("%d",&n);
    blocks=ceil(sqrt(n));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        cpy[i]=num[i];
        pos[i]=i/blocks;
    }
    sort(cpy+1,cpy+1+n);
    for(int i=1;i<=n;i++)//離散化
    {
        num[i]=lower_bound(cpy+1,cpy+1+n,num[i])-cpy;
    }
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&query[i].lef,&query[i].rig);
        query[i].id=i;
    }
    sort(query,query+m);
    int currL=1,currR=0,L,R;
    currSum=0;
    for(int i=0;i<m;i++)
    {
        L=query[i].lef;R=query[i].rig;
        while(currR<R)
        {
            currR++;
            add(num[currR],1);
            currSum+=currR-currL+1-sum(num[currR]);
        }
        while(currR>R)
        {
            currSum-=currR-currL+1-sum(num[currR]);
            add(num[currR],-1);
            currR--;
        }
        while(currL<L)
        {
           add(num[currL],-1);
           currSum-=sum(num[currL]-1);
           currL++;
        }
        while(currL>L)
        {
            currL--;
            add(num[currL],1);
            currSum+=sum(num[currL]-1);
        }
        ans[query[i].id]=currSum;
    }
    for(int i=0;i<m;i++)
    {
        printf("%d\n",ans[i]);
    }
}

E - NPY and girls
題意:
求區(qū)間的不同的排列方式鳖谈,同時,區(qū)間內(nèi)有重復的數(shù)字
題解:
假設區(qū)間L,R內(nèi)有x種數(shù)字n,分別為n1,n2,n3..nx阔涉,那么排列方式為(R-L+1)!/(n1!*n2!...nx!)
這里由于數(shù)值很大缆娃,結(jié)果取模,所以用到了乘法逆元

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b){ d=a;x=1;y=0;}
    else
    {
        gcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}
LL inv(LL a,LL n)
{
    LL d,x,y;
    gcd(a,n,d,x,y);
    return d==1?(x+n)%n:-1;
}
/*莫隊算法*/
const int MAXN=30010;
const LL MOD=1000000007;
int pos[MAXN];
int num[MAXN];
LL cnt[MAXN];
LL ans[MAXN];
LL inver[MAXN];
LL res;
struct Query
{
    int l,r,id;
    bool operator<(const Query &que) const
    {
        if(pos[l]==pos[que.l]) return r<que.r;
        return pos[l]<pos[que.l];
    }
};
Query query[MAXN];
void add(int position,LL len)
{
    res=res*len%MOD;
    cnt[num[position]]++;
    res=res*inver[cnt[num[position]]]%MOD;
}
void del(int position,LL len)
{
    res=res*inver[len]%MOD;
    res=res*cnt[num[position]]%MOD;
    cnt[num[position]]--;
}
int main()
{
    int t,n,m,blocks;
    scanf("%d",&t);
    for(long long i=1;i<MAXN;i++)
    {
        inver[i]=inv(i,MOD);
    }
    while(t--)
    {
        scanf("%d%d",&n,&m);
        blocks=ceil(sqrt(n+0.0));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",num+i);
            pos[i]=(i-1)/blocks;
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&query[i].l,&query[i].r);
            query[i].id=i;
        }
        sort(query,query+m);
        int currL=1,currR=0,L,R;
        res=1;
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<m;i++)
        {
            L=query[i].l;R=query[i].r;
            while(currR<R)
            {
                currR++;
                add(currR,currR-currL+1);
            }
            while(currR>R)
            {
                del(currR,currR-currL+1);
                currR--;
            }
            while(currL<L)
            {
                del(currL,currR-currL+1);
                currL++;
            }
            while(currL>L)
            {
                currL--;
                add(currL,currR-currL+1);
            }
            ans[query[i].id]=res;
        }
        for(int i=0;i<m;i++)
        {
            printf("%lld\n",ans[i]);
        }
    }
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瑰排,一起剝皮案震驚了整個濱河市贯要,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌椭住,老刑警劉巖崇渗,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡宅广,警方通過查閱死者的電腦和手機葫掉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乘碑,“玉大人挖息,你說我怎么就攤上這事∈薹簦” “怎么了套腹?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長资铡。 經(jīng)常有香客問我电禀,道長,這世上最難降的妖魔是什么笤休? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任尖飞,我火速辦了婚禮,結(jié)果婚禮上店雅,老公的妹妹穿的比我還像新娘政基。我一直安慰自己,他們只是感情好闹啦,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布沮明。 她就那樣靜靜地躺著,像睡著了一般窍奋。 火紅的嫁衣襯著肌膚如雪荐健。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天琳袄,我揣著相機與錄音江场,去河邊找鬼。 笑死窖逗,一個胖子當著我的面吹牛址否,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碎紊,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼在张,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了矮慕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤啄骇,失蹤者是張志新(化名)和其女友劉穎痴鳄,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缸夹,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡痪寻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年螺句,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片橡类。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛇尚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顾画,到底是詐尸還是另有隱情取劫,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布研侣,位于F島的核電站谱邪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏庶诡。R本人自食惡果不足惜惦银,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望末誓。 院中可真熱鬧扯俱,春花似錦、人聲如沸喇澡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撩幽。三九已至库继,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窜醉,已是汗流浹背宪萄。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榨惰,地道東北人拜英。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像琅催,于是被迫代替她去往敵國和親居凶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容