1. 程式人生 > >「CF86D」Powerful array 解題報告

「CF86D」Powerful array 解題報告

define 出現 加法 del getchar power 優化 端點 lar

題面

給出一個\(n\)個數組成的數列\(a\),有\(t\)次詢問,每次詢問為一個\([l,r]\)的區間,求區間內每種數字出現次數的平方×數字的值 的和

思路:

直接上莫隊咯 然後就T了

沒學過莫隊?!我也沒辦法

這道題的數據範圍在\(2e5\)的級別,有人會問莫隊肯定要炸啊 捏~

時限5000ms,那就可以亂搞了

但是!還是要加一些優化

如何優化?

1.對於算法本身的優化

由於莫隊可以說是一個塊狀暴力的算法,就是把區間劃分為\(\sqrt n\)塊然後在塊內暴力(到頭還是暴力)

我們可以把要查詢的區間當做點表示在平面直角坐標系上,RT:

技術分享圖片

就像這樣把詢問放在平面直角坐標系上,\(\large x\)

軸為詢問該區間的順序,\(\large y\)軸表示該區間的右端點,如果我們忽略區間的\(L\)值的影響,不難看出,雖然我們已經把區間劃分在一個塊裏了,但是還是有很多冗余的操作,如果要是這些冗余的操作少一點就好了,這當然可以啦!

我們可以讓塊內的區間按\(\large R\) 遞增,這樣能省很多時間,但是在區間過渡的時候,我們還是會做很多多余的操作,因為區間都是遞增的,這樣改變塊的時候就可能有一個很大的落差,就掉了下去,可以自己想象一下

技術分享圖片

為了避免上述的現象,我們可以讓區間像一個波浪一樣,這樣就很高效了,這樣的劃分方式叫做奇偶劃分應該是這麽叫的吧,還有一個是奇偶性剪枝

inline bool cmp(node a,node b){//代碼是關鍵,講了啥不重要(手動劃線)
    return (pos[a.l]==pos[b.l])?(pos[a.l]&1)?a.r<b.r:a.r>b.r : a.l<b.l;
}//千萬不要寫if,會T!

接著就是塊的大小,同樣影響速度,一般普通的塊的大小應該是\(\large \sqrt n\),但是,根據某奆佬研究,大小為\(\large n^{0.54}\)時更快,Orz

2.對於程序本身優化,說人話就是卡卡常

比如:

加點register

非遞歸函數前加個inline

不用快讀用fread

還有!乘法變加法……

然後就把最大時間卡到了622ms

啊哈哈哈哈哈

Code:

#include<bits/stdc++.h>
#define getchar() *(p++)//在快讀基礎上改一點就行了
#define Re register//卡常必備
#define ll long long
#define M 1000010
#define N 200010
using namespace std;
struct node{
    int l,r,i;
}b[N];//sum是數的多少,pos表示在哪塊
int a[N],pos[N],n,m,sum[M],l,r,block;
ll Ans[N],ans;
char bf[1<<25],*p;
int read(){
    Re int s=0;
    Re char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c))
    {
        s=(s<<1)+(s<<3)+c-'0';
        c=getchar();
    }
    return s;
}
inline bool cmp(node a,node b){//奇偶劃分
    return (pos[a.l]==pos[b.l])?(pos[a.l]&1)?a.r<b.r:a.r>b.r : a.l<b.l;
}//註意,這裏不要寫if語句,會T
inline void Add(Re ll x)
{
    sum[x]++;//有些人寫在前面,那樣的話就應該是+1
    ans+=(sum[x]+sum[x]-1)*x;//就是由原來的乘變成了加,手算一小部分也沒有關系啦
}
inline void Del(Re ll x)
{
    ans-=(sum[x]+sum[x]-1)*x;//這裏和上面也是一樣的
    sum[x]--;//為什麽我感覺上面-1會更慢呢~
}
int main()
{
    Re int i;
    bf[fread(bf,1,1<<25,stdin)]='\0';p=bf;//fread大法
    n=read();m=read();block=pow(n,0.54);//神奇的塊的大小
    for(i=1;i<=n;i++)
        a[i]=read(),pos[i]=i/block;
    for(i=1;i<=m;i++)
        b[i].l=read(),b[i].r=read(),b[i].i=i;
    sort(b+1,b+1+m,cmp);l=1;
    for(i=1;i<=m;i++)//然後上莫隊
    {
        while(r<b[i].r)
            Add(a[++r]);
        while(r>b[i].r)
            Del(a[r--]);
        while(l<b[i].l)
            Del(a[l++]);
        while(l>b[i].l)
            Add(a[--l]);
        Ans[b[i].i]=ans;
    }
    for(i=1;i<=m;i++)
        printf("%lld\n",Ans[i]);
    return 0;
}

再說一遍

最後註意,sort的時候,如果數組本來就有序了,sort會很慢,所以千萬不要在裏面加if了,容易T

「CF86D」Powerful array 解題報告