题意:n*m的平面内有K个不安全点,Q个询问位置在(x,y)的人能走到多少个点?走到:(x,y)和(x',y')之间的矩形中不包含不安全点。
标程:
1 #include<bits/stdc++.h> 2 #define mid ((l+r)>>1) 3 using namespace std; 4 int read() 5 { 6 int x=0,f=1;char ch=getchar(); 7 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 8 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 9 return x*f; 10 } 11 const int N=100005; 12 typedef long long ll; 13 int n,m,K,Q,T_Max[N<<2],tot,Max; 14 ll ans[N],T_sum[N<<2],T_suml[N<<2]; 15 struct node{int x,y,id,ty;node(){} node(int a,int b,int c,int d){x=a;y=b;id=c;ty=d;}}a[N*2],dg[N],q[N]; 16 bool cmp(const node &A,const node &B) {return A.x<B.x||A.x==B.x&&A.y<B.y||A.x==B.x&&A.y==B.y&&A.ty<B.ty;}//排序时||不要写成&&! 17 ll qry_sum(int k,int l,int r,int L,int R,int &mx)//表示当前区间的右边下界为mx时的折线下面积 18 { 19 if (L<=l&&r<=R) 20 { 21 if (mx>=T_Max[k]) return (ll)mx*(r-l+1); 22 if (l==r) return mx=T_Max[k]; 23 int tt=T_Max[k<<1|1]; ll res=0; 24 if (mx>=tt) 25 { 26 res+=(ll)mx*(r-mid);//mx会被修改,注意统计顺序! 27 res+=qry_sum(k<<1,l,mid,L,R,mx); 28 }else { 29 res+=T_suml[k]; 30 res+=qry_sum(k<<1|1,mid+1,r,L,R,mx); 31 } 32 mx=T_Max[k]; return res; 33 } 34 ll s=0; 35 if (R>mid) s+=qry_sum(k<<1|1,mid+1,r,L,R,mx);//先走右边,更新下界 36 if (L<=mid) s+=qry_sum(k<<1,l,mid,L,R,mx); 37 return s; 38 } 39 void ins(int k,int l,int r,int x,int y) 40 { 41 if (l==r) 42 { 43 if (y>T_Max[k]) T_Max[k]=T_sum[k]=y; 44 return; 45 } 46 if (x<=mid) ins(k<<1,l,mid,x,y);else ins(k<<1|1,mid+1,r,x,y); 47 int tt=T_Max[k<<1|1]; 48 T_suml[k]=qry_sum(k<<1,l,mid,l,mid,tt);//注意如果直接传入T_Max[k<<1|1]的话,在&下会被修改。 49 T_Max[k]=max(T_Max[k<<1],T_Max[k<<1|1]); 50 T_sum[k]=T_suml[k]+T_sum[k<<1|1]; 51 } 52 void work() 53 { 54 tot=0; 55 for (int i=1;i<=K;i++) a[++tot]=node(dg[i].x,dg[i].y,i,0); 56 for (int i=1;i<=Q;i++) a[++tot]=node(q[i].x,q[i].y,i,1); 57 sort(a+1,a+tot+1,cmp); 58 for (int i=1;i<=tot;i++) 59 { 60 if (!a[i].ty) ins(1,1,m,a[i].y,a[i].x); 61 else { 62 Max=0;ans[a[i].id]+=qry_sum(1,1,m,1,a[i].y,Max);//二维数点 63 Max=0;ans[a[i].id]-=qry_sum(1,1,m,a[i].y,a[i].y,Max);//减去重复的一条同行/同列轴 64 } 65 } 66 } 67 int main() 68 { 69 int T=read(); 70 while (T--) 71 { 72 n=read();m=read();K=read();Q=read(); 73 for (int i=1;i<=K;i++) dg[i].x=read(),dg[i].y=read(); 74 for (int i=1;i<=Q;i++) q[i].x=read(),q[i].y=read(),ans[i]=0;//组测清零 75 for (int i=0;i<4;i++) 76 { 77 work(); 78 for (int i=1;i<=(m<<2);i++) T_sum[i]=T_suml[i]=T_Max[i]=0; 79 for (int j=1;j<=K;j++) dg[j].x=n-dg[j].x+1,swap(dg[j].x,dg[j].y);//90度旋转坐标 80 for (int j=1;j<=Q;j++) q[j].x=n-q[j].x+1,swap(q[j].x,q[j].y); 81 swap(n,m); 82 } 83 for (int i=1;i<=Q;i++) printf("%lld\n",(ll)n*m-ans[i]); 84 } 85 return 0; 86 }
题解:李超树
就是为了此神题才去学习了一下李超树。
以x坐标的移动来看可以到达的点的范围,从起点轴向两边形成不规则菱形。
分四块讨论,以左下角为例,线段树上的点为y坐标,维护递减折线。左区间的高度要大于等于右区间,所以更新的时候要把右区间的高度也维护进左区间。求折线下面积和的时候,维护一个右方高度下界,先统计右区间再统计左区间。