zoukankan      html  css  js  c++  java
  • [loj#2113] [HNOI2015] 接水果

    题意简述

    给定 (n) 个节点的树。
    给定 (P) 个盘子,每个盘子是树上一条从 (u)(v) 的路径,有权值 (c)
    给定 (Q) 个水果,每个水果是树上一条从 (u)(v) 的路径。
    一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径。

    对每个水果,求可接住它的盘子中第 (k_i) 小的权值。

    (n,P,Q leq 40000)


    想法

    重点在于如何判断一条路径是否为另一条路径的子路径。

    假设我们要找包含路径 (u)(v) 的路径(即路径 (u)(v) 是哪些路径的子路径)。
    假设 (u)(dfs) 序更小,设 (u)(v)(lca)(L)
    有两种情况:
    1.(L eq u)(L eq v),即这条路径有拐弯
    易知包含它的路径一端在 (u) 子树中,一端在 (v) 子树中
    2.(L=u)
    (z) 为这条路径上 (u) 下面的第一个点。则包含这条路径的路径一端在 (v) 子树中,另一端在 非(z)子树 的那些点中(画图可发现)

    把每条路径投射到二维平面上的点 ((u,v))(u,v) 为路径的两端点且(u)(dfs) 序更小
    发现上面两种情况对应的路径是二维平面上的一些矩形。

    再回来考虑第 (k) 大,显然可以二分答案,然后扫描线+树状数组判断。
    有多组询问,用整体二分。


    总结

    思想

    1.树上路径投射成二维平面上的点 ((u,v))
    2.扫描线不一定配线段树,树状数组常数更小。

    码力

    此处应划重点!!
    1.数组大小!!!!!!!!!!特别注意不要以为有的注意了就说明所有都注意了,所以都还要好好检查!!!!!!
    2.(a!=b.a?a<b.a:c>b.c) 这个判断一定要写清楚……别写反了……
    3.整体二分中全局变量与局部变量的问题!!比如 (cnt1,cnt2) 应设为内部的变量,不然递归一般后大小变了
    4.这个题差分比较多,想清楚。。
    5.树状数组!我咋能忘了咋用呢。。。此题中是区间修改、单点查询,所以查询时不用减前面的了……写之前要搞清楚
    6.别忘了离散化哦


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<map>
    
    using namespace std;
    
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return x; 
    }
    
    const int N = 40005;
    
    int n,pla,fru;
    
    struct node{
    	int v;
    	node *nxt;
    }pool[N*2],*h[N];
    int cnt;
    void addedge(int u,int v){
    	node *p=&pool[++cnt],*q=&pool[++cnt];
    	p->v=v;p->nxt=h[u];h[u]=p;
    	q->v=u;q->nxt=h[v];h[v]=q;
    }
    
    int dfn[N],dep[N],fa[N][18],out[N],tot;
    void dfs(int u){
    	int v;
    	dfn[u]=++tot;
    	for(node *p=h[u];p;p=p->nxt)
    		if(!dfn[v=p->v]) {
    			fa[v][0]=u; dep[v]=dep[u]+1;
    			for(int j=1;j<16;j++) fa[v][j]=fa[fa[v][j-1]][j-1];
    			dfs(v);
    		}
    	out[u]=tot;
    }
    int fst(int u,int v){
    	for(int i=15;i>=0;i--) if(dep[fa[v][i]]>dep[u]) v=fa[v][i];
    	return v;
    }
    
    int mmp[N];
    map<int,int> mp;
    struct data{
    	int a,l,r,c,k,id; //c==-1?fruit:plate
    	bool operator < (const data &b) const{
    		return a!=b.a?a<b.a:c>b.c;
    	}
    }d[N*6],t1[N*6],t2[N*6]; /*all N*6*/
    int num;
    
    int C[N];
    void add(int x,int y) { while(x<=n) C[x]+=y,x+=x&(-x); }
    int sum(int x) { int ret=0; while(x) ret+=C[x],x-=x&(-x); return ret; }
    
    int ans[N];
    void work(int l,int r,int L,int R){
    	if(L>R || l>r) return;
    	if(l==r){
    		for(int i=L;i<=R;i++) if(d[i].c==-1) ans[d[i].id]=l;
    		return;
    	}
    	int flag=0;
    	for(int i=L;i<=R;i++) if(d[i].c==-1) flag=1;
    	if(!flag) return;
    	
    	int mid=(l+r)>>1,s;
    	int cnt1=0,cnt2=0; //inner
    	for(int i=L;i<=R;i++){
    		if(d[i].c==-1){
    			s=sum(d[i].l); /*dandian query*/
    			if(s<d[i].k) d[i].k-=s,t2[++cnt2]=d[i];
    			else t1[++cnt1]=d[i];
    		}
    		else{
    			if(d[i].c>mid) t2[++cnt2]=d[i];
    			else {
    				t1[++cnt1]=d[i];
    				add(d[i].l,d[i].k); add(d[i].r+1,-d[i].k); /*r+1*/
    			}
    		}
    	}
    	for(int i=1;i<=cnt1;i++)
    		if(t1[i].c!=-1) {
    			add(t1[i].l,-t1[i].k);
    			add(t1[i].r+1,t1[i].k); /*r+1*/
    		}
    	for(int i=1;i<=cnt1;i++) d[L+i-1]=t1[i];
    	for(int i=1;i<=cnt2;i++) d[L+cnt1+i-1]=t2[i]; 
    	work(l,mid,L,L+cnt1-1); work(mid+1,r,L+cnt1,R);
    }
    
    int main()
    {
    	n=read(); pla=read(); fru=read();
    	for(int i=1;i<n;i++) addedge(read(),read());
    	dep[1]=1; dfs(1);
    	int mx=0,u,v,z,c;
    	for(int i=1;i<=pla;i++){
    		u=read(); v=read(); c=read(); mmp[i]=c;
    		if(dfn[u]>dfn[v]) swap(u,v);
    		if(dfn[v]<=out[u]) {//u->v:(1,dfn[v])-(dfn[z]-1,out[v]);(dfn[v],out[z]+1)-(out[v],n)
    			z=fst(u,v); 
    			if(dfn[z]-1>0){
    				++num; d[num].a=1; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=1;
    				++num; d[num].a=dfn[z]; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=-1;
    			}
    			if(out[z]+1<=n){
    				++num; d[num].a=dfn[v]; d[num].l=out[z]+1; d[num].r=n; d[num].c=c; d[num].k=1;
    				++num; d[num].a=out[v]+1; d[num].l=out[z]+1; d[num].r=n; d[num].c=c; d[num].k=-1;
    			}
    		}
    		else{ //(dfn[u],dfn[v])-(out[u],out[v])
    			++num; d[num].a=dfn[u]; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=1;
    			++num; d[num].a=out[u]+1; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=-1;
    		}
    	}
    	for(int i=1+num;i<=fru+num;i++){
    		u=read(); v=read(); if(dfn[u]>dfn[v]) swap(u,v);
    		d[i].a=dfn[u]; d[i].l=d[i].r=dfn[v]; d[i].c=-1; d[i].k=read(); d[i].id=i-num; /*dfn*/
    	}
    	num+=fru;
    	
    	sort(mmp+1,mmp+1+pla);
    	mx=unique(mmp+1,mmp+1+pla)-mmp-1;
    	for(int i=1;i<=mx;i++) mp[mmp[i]]=i;
    	for(int i=1;i<=num;i++) 
    		if(d[i].c!=-1) d[i].c=mp[d[i].c];
    	
    	sort(d+1,d+1+num); 
    	work(1,mx,1,num);
    	for(int i=1;i<=fru;i++) printf("%d
    ",mmp[ans[i]]);
    	
    	return 0;
    }
    
  • 相关阅读:
    iOS 日期格式
    时间复杂度、空间复杂度
    转载 -- 算法题
    奇奇怪怪的Bug
    iOS12中推送通知新特性
    iOS开发网络篇—Socket编程
    iOS:菜单控制器和菜单项:UIMenuController和UIMenuItem
    关于textField输入光标颜色及cleanButton大小和颜色的设置
    腾讯云开发微信小程序使用体验
    简单理解JavaScript原型链
  • 原文地址:https://www.cnblogs.com/lindalee/p/12995038.html
Copyright © 2011-2022 走看看