zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    nginx能访问html静态文件但无法访问php文件
    LeetCode "498. Diagonal Traverse"
    LeetCode "Teemo Attacking"
    LeetCode "501. Find Mode in Binary Search Tree"
    LeetCode "483. Smallest Good Base" !!
    LeetCode "467. Unique Substrings in Wraparound String" !!
    LeetCode "437. Path Sum III"
    LeetCode "454. 4Sum II"
    LeetCode "445. Add Two Numbers II"
    LeetCode "486. Predict the Winner" !!
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6427604.html
Copyright © 2011-2022 走看看