bzoj2716: [Violet 3]天使玩偶
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 1000005 7 #define maxk 1000005 8 using namespace std; 9 10 int n,m,ans[maxn],sum[maxk],maxx,maxy; 11 const int inf=maxk*4; 12 struct date{ 13 int op,x,y,id; 14 }list[maxn],a[maxn]; 15 16 bool comp(date x,date y){ 17 return x.x<y.x; 18 } 19 20 int lowbit(int x){ 21 return x&(-x); 22 } 23 24 void add(int x,int y){ 25 for (int i=x;i<=maxy;i+=lowbit(i)){ 26 sum[i]=max(sum[i],y); 27 } 28 } 29 30 int query(int x){ 31 int temp=0; 32 for (int i=x;i>0;i-=lowbit(i)){ 33 temp=max(temp,sum[i]); 34 } 35 if (!temp) return -inf; 36 return temp; 37 } 38 39 void cdq_solve(int l,int r){ 40 if (l==r) return; 41 int mid=(l+r)/2; 42 cdq_solve(l,mid),cdq_solve(mid+1,r); 43 sort(a+l,a+mid+1,comp),sort(a+mid+1,a+r+1,comp); 44 int temp=0; 45 for (int i=l,j=mid+1;j<=r;){ 46 for (;a[i].op==2&&i<=mid;i++); 47 for (;a[j].op==1&&j<=r;j++); 48 if (i<=mid&&a[i].x<=a[j].x) temp=i,add(a[i].y,a[i].x+a[i].y),i++; 49 else if (j<=r) ans[a[j].id]=min(ans[a[j].id],a[j].x+a[j].y-query(a[j].y)),j++; 50 } 51 for (int i=l;i<=temp;i++) if (a[i].op==1){ 52 for (int j=a[i].y;j<=maxy;j+=lowbit(j)) sum[j]=0; 53 } 54 } 55 56 void read(int &x){ 57 x=0;int f=1;char ch; 58 for (ch=getchar();ch>'9'||ch<'0';ch=getchar()) if (ch=='-') f=-1; 59 for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; 60 x*=f; 61 } 62 63 int main(){ 64 int op,x,y; 65 read(n),read(m); 66 maxx=maxy=0,memset(sum,0,sizeof(sum)); 67 for (int i=1;i<=n+m;i++) ans[i]=inf; 68 for (int i=1;i<=n;i++){ 69 read(x),read(y); 70 maxx=max(maxx,x),maxy=max(maxy,y); 71 list[i].op=1,list[i].x=x,list[i].y=y; 72 } 73 for (int i=n+1;i<=n+m;i++){ 74 read(op),read(x),read(y); 75 list[i].op=op,list[i].x=x,list[i].y=y,list[i].id=i; 76 maxx=max(maxx,x),maxy=max(maxy,y); 77 } 78 maxx++,maxy++; 79 n+=m; 80 for (int i=1;i<=n;i++) a[i]=list[i]; 81 cdq_solve(1,n); 82 for (int i=1;i<=n;i++) a[i]=list[i],a[i].x=maxx-a[i].x; 83 cdq_solve(1,n); 84 for (int i=1;i<=n;i++) a[i]=list[i],a[i].y=maxy-a[i].y; 85 cdq_solve(1,n); 86 for (int i=1;i<=n;i++) a[i]=list[i],a[i].x=maxx-a[i].x,a[i].y=maxy-a[i].y; 87 cdq_solve(1,n); 88 for (int i=1;i<=n;i++) if (list[i].op==2) printf("%d\n",ans[i]); 89 return 0; 90 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2716
题意:初始时给定平面内n个点的坐标,构成一个点集,以及m个操作,每个操作为一下两种之一:
1.在该点集中加入一个新点;
2.给定一个点的坐标,询问该点集中的点到该点的曼哈顿距离最小值。
做法:第一种做法是裸上k-d tree,现在我还不会写……;
第二种做法是cdq分治+排序+树状数组(+读入优化,因为我有几个同学被卡常了,有没有觉得特别坑-.-),cdq分治的关键就在于如何高效地用第一个区间的修改来更新第二个区间的询问。首先,我们把初始的n个点也当成修改操作,对于一个询问,影响它的修改操作可能在它的左上,右上,左下,右下,我们在这里只考虑左下的情况(因为其他三种用maxx,maxy搞一搞就可以搞到左下方来,这样做并不影响相对距离,即不影响答案),那如何处理修改在询问左下方的情况呢?
我的方法比较辣鸡qaq,跑了75s,rank1可是10s不到啊,捂脸>.<;
对于修改都在左下方了,dist=x-X+y-Y,(x,y)为询问的点的坐标,(X,Y)为修改的点的坐标,式子可化简为dist=(x+y)-(X+Y),(x+y)确定,我们需要统计的就是位于其左下方的点中,横纵坐标之和最大的,这里我们将两个区间中的点分别按x坐标升序排序,在树状数组中更新答案,及时更新询问的答案即可。过程中用两个指针维护即可。
为什么要转化到左下方呢?因为方便插入,和查询,树状数组只能支持查询前缀最大(小)值。
cdq分治+树状数组+排序