BZOJ4009 [HNOI2015]接水果
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4009
正解:整体二分+扫描线+树状数组
解题报告:
考虑哪些水果会被当前盘子接到,显然只能是一端位于x子树中,另一端位于y子树中的水果。
如果把两个dfs序区间看成横纵坐标,这种情况对应的就是一个矩阵。
如果x是y的祖先,那么就是两个矩阵(拆分一下)。
问题转化为,求覆盖每个水果(矩阵中的点)的盘子(一个矩阵)中的第k小权值。
所以我们就可以整体二分辣!
我开始想到整体二分之后就卡住了,因为不知道怎么维护一个大矩阵的统计。
事实上,只要按x排序,然后树状数组统计y,把一个矩阵拆成4个点事件,2个+1,2个-1,就可以用扫描线从上往下扫一遍得到答案。
这种方法比较巧妙,结合整体二分,常数也挺小的。
我开始WA了很久,因为整体二分那里,我没有考虑x坐标在每个区间内的相对关系必须始终保持!我是从r往前存丢到右边的询问,所以肯定会WA!这一点在做整体二分的时候一定要注意!
//It is made by ljh2000 #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> #include <string> #include <complex> using namespace std; typedef long long LL; const int MAXN = 40011; const int MAXM = 80011; int n,m,Q,ecnt,first[MAXN],to[MAXM],next[MAXM],f[MAXN][17],dfn[MAXN],last[MAXN],deep[MAXN]; int Mcnt,ans[MAXN],c[MAXN<<1]; struct Matrix{ int lx,rx,ly,ry,val;}b[MAXN<<2];//矩形的左上角和右下角 struct ask{ int x,y,k,id; }q[MAXN],tem[MAXN<<1],tem2[MAXN<<1]; struct Thing{ int x,y,val; }wyh[MAXN<<3]; inline bool cmp(Matrix pp,Matrix pq){ return pp.val<pq.val; } inline bool cmp2(ask pp,ask pq){ return pp.x<pq.x; } inline bool cmp3(Thing pp,Thing pq){ return pp.x<pq.x; } inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; } inline void add(int x,int val){ while(x<=n) { c[x]+=val; x+=x&(-x); } } inline int query(int x){ int tot=0; while(x>0) { tot+=c[x]; x-=x&(-x); } return tot; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline void dfs(int x,int fa){//预处理dfs序和倍增 dfn[x]=++ecnt; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; deep[v]=deep[x]+1; f[v][0]=x; dfs(v,x); } last[x]=ecnt; } inline int lca(int x,int y){ if(deep[x]<deep[y]) swap(x,y);/*!!!*/ int t=0; while((1<<t)<=deep[x]) t++; t--; for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return x; for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } inline void solve(int l,int r,int L,int R){//询问处理区间为[l,r],矩阵区间为[L,R] if(l>r) return ; if(L==R) { for(int i=l;i<=r;i++) ans[q[i].id]=b[L].val; return ; } int cnt=0,now=0,nowk,nowl=0,nowr=0,mid=(L+R)>>1; //把每个矩形拆分成四个点事件,从上往下执行扫描线,树状数组维护区间sum //在图上画一画能够发现,这样从上往下做可以统计经过这个点的矩阵个数 //并且只要从头做到尾就相当于是执行了撤销操作,因为-1操作全都被执行了 for(int i=L;i<=mid;i++) { wyh[++cnt]=(Thing){b[i].lx,b[i].ly,1}; wyh[++cnt]=(Thing){b[i].lx,b[i].ry+1,-1};//不能因为越界就不处理! wyh[++cnt]=(Thing){b[i].rx+1,b[i].ly,-1}; wyh[++cnt]=(Thing){b[i].rx+1,b[i].ry+1,1}; } sort(wyh+1,wyh+cnt+1,cmp3); for(int i=l;i<=r;i++) { while(now<cnt && wyh[now+1].x<=q[i].x) now++,add(wyh[now].y,wyh[now].val); nowk=query(q[i].y); if(q[i].k<=nowk) tem[nowl++]=q[i]; else { q[i].k-=nowk; tem2[nowr++]=q[i]; }//不能从r放起!x顺序会失去!!! } for(;now<cnt;) now++,add(wyh[now].y,wyh[now].val); for(int i=0;i<nowl;i++) q[i+l]=tem[i]; for(int i=0;i<nowr;i++) q[l+nowl+i]=tem2[i]; solve(l,l+nowl-1,L,mid); solve(l+nowl,r,mid+1,R); } inline void work(){ n=getint(); m=getint(); Q=getint(); int x,y,z,LCA,yfa; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); } ecnt=0; dfs(1,0); for(int j=1;j<=16;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; for(int i=1;i<=m;i++) {//将每个盘子的影响范围考虑成一个或者两个矩形 x=getint(); y=getint(); z=getint(); LCA=lca(x,y); if(dfn[x]>dfn[y]) swap(x,y); if(LCA==x) {//考虑拆成两个矩形 yfa=y; for(int j=16;j>=0;j--) if(deep[yfa]-(1<<j)>deep[x]) yfa=f[yfa][j]; //b[++Mcnt]=(Matrix){1,dfn[yfa]-1,dfn[y],last[y],z};//直接拆分成两个矩形,显然不会有交集,所以可以分开计算贡献 //b[++Mcnt]=(Matrix){dfn[y],last[y],last[yfa]+1,n,z};//注意只统计x<y的情况! if(dfn[yfa]>1) b[++Mcnt]=(Matrix){1,dfn[yfa]-1,dfn[y],last[y],z};//直接拆分成两个矩形,显然不会有交集,所以可以分开计算贡献 if(last[yfa]<n) b[++Mcnt]=(Matrix){dfn[y],last[y],last[yfa]+1,n,z};//注意只统计x<y的情况! } else b[++Mcnt]=(Matrix){dfn[x],last[x],dfn[y],last[y],z}; } for(int i=1;i<=Q;i++) { q[i].x=dfn[getint()]; q[i].y=dfn[getint()]; q[i].k=getint(); q[i].id=i; if(q[i].x>q[i].y) swap(q[i].x,q[i].y); } sort(b+1,b+Mcnt+1,cmp); sort(q+1,q+Q+1,cmp2); solve(1,Q,1,Mcnt); for(int i=1;i<=Q;i++) printf("%d\n",ans[i]); } int main() { work(); return 0; }
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!