本文版权归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;
}
原文:http://www.cnblogs.com/ljh2000-jump/p/6427604.html