• 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
    ",ans[i]);
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    【C#图解教程学习笔记】第13章 委托
    TreeView树,全选,反选,平级选操作
    C# 字符串和字节数组转换
    WebServices 使用Session
    Win8.1下Flash Builder 提示: 找不到所需的Adobe Flash Player调试器版本,解决办法
    C# winform DataGridView 绑定数据的的几种方法
    .NET Core GB2312
    [WPF 学习] 7.2 模板打印
    [WPF 学习] 7.1 多页打印
    [WPF 学习] 7 打印相关的东东
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6427604.html
Copyright © 2020-2023  润新知