1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x44分塊 AcWing磁力塊

《演算法競賽進階指南》0x44分塊 AcWing磁力塊

題目連結:https://www.acwing.com/problem/content/252/

題目給出一些點的座標,質量,磁力和吸引半徑,初始時刻只有一個磁石在(x0,y0)位置,可以通過磁石吸引其他磁石,然後選擇磁石繼續進行吸引,問最終可以得到多少磁石?

由於磁石的吸引要滿足距離小於吸引半徑,質量小於等於其磁力,所以可以按照質量進行排序分塊,第二個約束在分塊內進行排序,儲存分塊的最大的質量。對於可以拿的磁石都放入佇列,進行BFS,完全小於其質量的只要從每段的頭部開始取就可以,每段的頭部在取完之後要變動到最終不可取的位置。而質量不完全小於等於隊首元素的磁力的時候就需要對每個磁石單獨進行判斷。在對完全滿足的進行判斷的時候,是否訪問過的判斷要在while迴圈中,要保證L[i]在滿足兩個條件的情況下必須後移。還要注意平方的時候都要變成ll進行判斷,否則會wa。

程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int maxn = 250010;
const int T = 550;
struct node{
    int x,y,m,p,r;
}p[maxn],q[maxn];
int n;
int L[maxn],R[maxn],M[maxn];
bool vis[maxn];
inline bool
cmp0(node& a,node& b){ return a.m<b.m; } inline ll dis2(node& a){ return (ll)(a.x-q[0].x)*(a.x-q[0].x)+(ll)(a.y-q[0].y)*(a.y-q[0].y); } inline bool cmp1(node& a,node& b){ return dis2(a)<dis2(b); } int main(){ scanf("%d%d%d%d%d",&q[0].x,&q[0].y,&q[0].p,&q[0
].r,&n); for(int i=1;i<=n;i++) scanf("%d%d%d%d%d",&p[i].x,&p[i].y,&p[i].m,&p[i].p,&p[i].r); sort(p+1,p+n+1,cmp0);//按照質量進行排序 int t=sqrt(n); int len=t?n/t:n; for(int i=1;i<=t;i++){ L[i]=(i-1)*len+1; R[i]=i*len; M[i]=p[R[i]].m;//分塊內部的最大質量 sort(p+L[i],p+R[i]+1,cmp1);//按照距離進行組內的二次排序 } if(R[t]<n){ L[t+1]=R[t]+1; R[++t]=n; M[t]=p[n].m; sort(p+L[t],p+R[t]+1,cmp1); } int ans=0; int l=0,r=1; while(l<r){ int k=0; for(int i=1;i<=t;i++){//找到最後一個塊k,前面的都是<=隊頭磁石的質量的 if(M[i]<=q[l].p)k=i; else break; } for(int i=1;i<=k;i++){ while(L[i]<=R[i] && dis2(p[L[i]])<=(ll)q[l].r*q[l].r){ if(!vis[L[i]]){//在單獨處理的塊塊中被訪問 vis[L[i]]=1;//同一個磁石不會算兩次 ans++; q[r++]=p[L[i]]; } L[i]++; } } if(t!=k){//不是最後一個塊 k++;//單獨掃描當前塊中的每個元素 for(int i=L[k];i<=R[k];i++){ if(!vis[i] && p[i].m<=q[l].p && dis2(p[i])<=(ll)q[l].r*q[l].r){ vis[i]=1; ans++; q[r++]=p[i]; } } } l++; } cout<<ans<<endl; return 0; }