Codeforces - 848C - Goodbye Souvenir(CDQ分治)

Codeforces - 848C - Goodbye Souvenir

設數列中第 i 個點前一個與 i 顏色相同的點是 prev[i]
那麼每次詢問的答案是

i=lprev[i]lriprev[i]

如果考慮二維平面上點 (i,prev[i]) 的權值爲 iprev[i] 。那麼詢問轉化爲求兩端點分別爲 (l,l)(r,r) 的正方形內框住的點的權值和。
x=iy=prev[i] ,且考慮到 yx 恆成立,那麼上式轉化爲
yilxirval[i]

由於 yixir ,可以知道詢問的區域應該是 x[0,r]y[l,r] 這一段區域。
用樹狀數組處理 y 這一維,cdq處理 x 這一維就可以做了。

CDQ分治教程
cdq分治時間,每次處理左邊的修改對右邊詢問的影響。
prev 可以用 set 來維護。
時間複雜度最大爲 O(nlognlogn)

其實也可以直接求正方形。。如果這樣的化把詢問轉化成四個角的詢問就行了。。。這樣應該更好理解一點。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
int n,a[N],m,vis[N],tot;
ll c[N],ans[N];
bool hq[N];
set<int> s[N];
struct Node
{
    //對於修改,x爲橫軸,y爲縱軸,f爲修改值,id爲0
    //對於詢問,x爲右端點,y爲左端點,f爲0,id爲詢問id
    int x,y,f,id;
}Q[N*6],tmp[N*6];
void modify(int x,int y)
{
    if(a[x]==y) return ;
    auto i=s[a[x]].find(x);
    int p=0;
    if(i!=s[a[x]].begin())
    {
        --i;p=*i;++i;
        Q[m++]=(Node){x,p,p-x,0};
    }
    ++i;
    if(i!=s[a[x]].end())
    {
        Q[m++]=(Node){*i,x,x-*i,0};
        if(p) Q[m++]=(Node){*i,p,*i-p,0};
    }
    s[a[x]].erase(x);
    a[x]=y;
    i=s[y].insert(x).first;
    p=0;
    if(i!=s[y].begin())
    {
        --i;p=*i;++i;
        Q[m++]=(Node){x,p,x-p,0};
    }
    ++i;
    if(i!=s[y].end())
    {
        if(p) Q[m++]=(Node){*i,p,p-*i,0};
        Q[m++]=(Node){*i,x,*i-x,0};
    }
}
int lowbit(int x) { return x&-x; }
void update(int x,int y)
{
    for(;x<=n;x+=lowbit(x))
        if(vis[x]!=tot) vis[x]=tot,c[x]=y;
        else c[x]+=y;
}
ll query(int x)
{
    ll res=0;
    for(;x>0;x-=lowbit(x))
        res+=(vis[x]==tot?c[x]:0);
    return res;
}
void solve(int L,int R)
{
    if(L>=R) return ;
    int mid=(L+R)>>1;
    solve(L,mid);
    solve(mid+1,R);
    ++tot;
    int t1=L,t2=mid+1;
    for(int i=L;i<=R;++i)
    {
        if(t2>R||(t1<=mid&&Q[t1].x<=Q[t2].x)) //控制詢問的點在(x,x) 的右下角
        {
            tmp[i]=Q[t1++];
            if(tmp[i].id==0) update(n-tmp[i].y+1,tmp[i].f);
        }
        else
        {
            tmp[i]=Q[t2++];
            if(tmp[i].id) ans[tmp[i].id]+=query(n-tmp[i].y+1); //詢問 y=y上方的部分。
        }
    }
    for(int i=L;i<=R;++i) Q[i]=tmp[i];
}
//二維座標系中若x=i,那麼y=pre[i]。y<x恆成立。
int main()
{
    int q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        auto it=s[a[i]].insert(i).first;
        if(it!=s[a[i]].begin()) --it,Q[m++]=(Node){i,*it,i-*it,0};
    }
    for(int i=1;i<=q;++i)
    {
        int op,l,r;
        scanf("%d%d%d",&op,&l,&r);
        if(op==1) modify(l,r);
        else hq[i]=true,Q[m++]=(Node){r,l,0,i};
    }
    solve(0,m-1);
    for(int i=1;i<=q;++i) if(hq[i]) printf("%I64d\n",ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章