1. 程式人生 > >CodeForces 1045G AI robots(CDQ分治 + 樹狀陣列 + 單調佇列)

CodeForces 1045G AI robots(CDQ分治 + 樹狀陣列 + 單調佇列)

大致題意:有很多個機器人,他們要相互交流有一些限制條件。首先是,兩個人要相互能夠能夠看到;其次,兩個人的智商的差不超過K。現在給出每個機器人的視力範圍和他們的智商,現在問你總共有多少對機器人能夠相互交流。

首先來看下總共有多少個限制條件。由於是要求雙方都能夠看到,所以顯然是要按照視野半徑去排序的。然後要求兩個人的智商差要在一定的範圍內的,所以也要按照智商去排序。另外還要跟自己的位置有關。根據這個,我們可以構造出分治的大致方法:首先按照半徑去排序,半徑大的在前面,因為這樣如果後面的東西能夠看到前面,那麼前面的東西一定能看到後面,符合cdq分治前面更新後面的要求;然後分治的過程中歸併排序按照智商的大小去排序,因為這樣可以利用一些單調性,同時好確定智商差;最後就是用一個離散化的樹狀陣列去維護每個位置的機器人數量。

前面和最後的都好說,關鍵是如何利用智商差去求能夠相互看到的機器人的對數。這裡我們用到了單調佇列。由於是按照智商的大小來進行分治的。所以前一半和後一半已經是按照智商的大小排好序了的,所以說我們可以利用單調性。根據cdq分治的原則,前面一半去更新後面一半,所以我們考慮對於後一半的每個機器人,單獨計算貢獻。對於後一半的第i個機器人,我可以在前一半確定一個區間[j,k],在這個區間內的所有機器人與機器人i的智商差不超過K。把這個區間內的所有機器人新增到樹狀陣列中,然後貢獻就是機器人i視野範圍內的機器人數目。然後再往後考慮後一半的其他機器人。由於前一半和後一半的智商是遞減的,所以從i移動到i+1區間的移動只會單調往後移動,符合單調佇列性質。這樣前一半的每一個機器人只會進出佇列各一次,對應加入和移出樹狀陣列各一次。所以這個部分均攤的時間複雜度是O(NlogN)的,這個log來自樹狀陣列。

如此我們就求出了合併的時候前面對後面產生的貢獻。總的時間複雜度就是O(NlogNlogN)。具體見程式碼:

#include<bits/stdc++.h>
#define LL long long
#define N 100010

using namespace std;

struct node{int x,r,iq,L,R;} p[N],tmp[N];
int n,tot,K,x[N<<2],q[N]; LL ans=0;

struct BinaryIndexedTree
{
    int c[N<<2];

    inline void update(int x,int k)
    {
        x=min(x,tot);
        for(;x<=tot;x+=x&-x) c[x]+=k;
    }

    inline int getsum(int x)
    {
        int ans=0;
        for(;x>0;x-=x&-x) ans+=c[x];
        return ans;
    }

} BIT;

bool cmp1(node a,node b)
{
    return a.r>b.r;
}

bool cmp2(node a,node b)
{
    return a.iq>b.iq;
}

void cdq(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int h=0,t=0;
    for(int i=mid+1,j=l;i<=r;i++)
    {
        while(j<=mid&&p[j].iq-p[i].iq>K) j++;
        while(j<=mid&&abs(p[j].iq-p[i].iq)<=K)
        {
            BIT.update(p[j].x,1);
            q[t++]=j++;
        }
        while(h<t&&abs(p[q[h]].iq-p[i].iq)>K)
        {
            BIT.update(p[q[h]].x,-1);
            h++;
        }
        ans+=BIT.getsum(p[i].R)-BIT.getsum(p[i].L-1);
    }
    for(;h<t;h++) BIT.update(p[q[h]].x,-1);
    inplace_merge(p+l,p+mid+1,p+r+1,cmp2);
}

int main()
{
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)
    {
        int X,Y,Z;
        scanf("%d%d%d",&X,&Y,&Z);
        p[i]=node{X,Y,Z,0,0};
        x[++tot]=X; x[++tot]=X-Y; x[++tot]=X+Y;
    }
    sort(x+1,x+tot+1);
    tot=unique(x+1,x+tot+1)-x-1;
    for(int i=1;i<=n;i++)
    {
        p[i].L=lower_bound(x+1,x+tot+1,p[i].x-p[i].r)-x;
        p[i].R=lower_bound(x+1,x+tot+1,p[i].x+p[i].r)-x;
        p[i].x=lower_bound(x+1,x+tot+1,p[i].x)-x;
    }
    sort(p+1,p+n+1,cmp1);
    cdq(1,n);
    printf("%lld\n",ans);
    return 0;
}