• P3242 [HNOI2015] 接水果


    题目链接

    题意分析

    这是一道花了一晚上+上午两个小时+下午两个小时干出来的题

    真心不容易

    首先 一看这道题的题面 对于多组询问求解第k小 就让人想到了整体二分

    但是我们需要处理一个棘手的问题 怎么判断一条路径 u→v 被另外一条路径 x→y 覆盖呢?


    这个我们我们可以使用dfs序解决

    这里我们令dfn[u]为u的dfs序 siz[u]为以u为根的子树大小

    令dfn[u]<dfn[v] dfn[x]<dfn[y]

    1.lca(u,v)=u 也就是说u→v是一条深度单调递增的链

    无标题.png

    我们令z是从u到v所经过的第一个点

    那么x与y必然一个属于以z为根的子树外的点 一个属于以v为根的子树

    就是满足以下两个条件中的一个

    1).1≤dfn[x]<dfn[z] dfn[v]≤dfn[y]≤dfn[v]+siz[v]-1

    无标题.png

    2).dfn[v]≤dfn[x]≤dfn[v]+siz[v]-1 dfn[z]+siz[z]-1<dfn[y]≤n

    无标题.png

    2.lca(u,v)!=u 也就是说u→v是一条深度先减少后增加的链

    无标题.png

    那么x与y必然一个属于以u为根的子树 一个属于以v为根的子树

    无标题.png


    按照上述结论我们可以发现 实际上这就是一个二维数点问题 判断一个点被多少个矩形覆盖了 使用扫描线方法加以解决

    这就是我们拿来进行整体二分的判断方式

    这道题很多人说需要卡常 比如说使用树剖代替倍增求lca以及u→v的第一个点z

    但是本蒟蒻使用倍增过了 可能是因为我比较帅吧(doge)

    CODE:

    #include<bits/stdc++.h>
    #define N 500010
    using namespace std;
    int n,m,q,tot,cnt;
    int to[N],nex[N],head[N];
    int dep[N],fa[N][22];
    int dfn[N],wt[N],siz[N];
    vector<int> G[N];
    struct Edge
    {
    	int u,v,w,knd,ex;
    }e[N];
    struct Temp
    {
    	int le,ri,at,d,knd;
    	friend bool operator < (const Temp &A,const Temp &B)
    	{return A.at==B.at ? A.knd<B.knd : A.at<B.at;}//我们需哦保证先修改再询问
    }et[N];
    int res[N],tre[N],vis[N];
    struct Query
    {
    	int u,v,k,id,ans;
    	friend bool operator < (const Query &A,const Query &B)
    	{return A.id<B.id;}
    }que[N],cdy[N],wzy[N];
    void add(int x,int y)
    {to[++tot]=y;nex[tot]=head[x];head[x]=tot;}
    void dfs(int now,int fat) 
    {
    	dep[now]=dep[fat]+1;fa[now][0]=fat;
    	for(int i=1;(1<<i)<=dep[now];++i)
    	fa[now][i]=fa[fa[now][i-1]][i-1];
    	
    	siz[now]=1;dfn[now]=++cnt;wt[cnt]=now;
    	
    	for(int i=head[now];i;i=nex[i])
    	{
    		int v=to[i];
    		if(v==fat) continue;
    		dfs(v,now);
    		siz[now]+=siz[v];
    	}
    }
    int LCA(int nowx,int nowy)
    {
    	if(dep[nowx]<dep[nowy]) swap(nowx,nowy);
    	for(int i=20;i>=0;--i)
    	if(dep[fa[nowx][i]]>=dep[nowy]) nowx=fa[nowx][i];
    	if(nowx==nowy) return nowx;
    	for(int i=20;i>=0;--i)
    	if(fa[nowx][i]!=fa[nowy][i])
    	nowx=fa[nowx][i],nowy=fa[nowy][i];
    	return fa[nowx][0];
    }
    void update(int x,int d)
    {for(;x<=n;x+=x&-x) tre[x]+=d;}
    int query(int x)
    {int tmp=0;for(;x;x-=x&-x) tmp+=tre[x];return tmp;}
    void solve(int lenow,int rinow,int le,int ri)
    {
    	if(lenow==rinow)
    	{
    		for(int i=le;i<=ri;++i) que[i].ans=lenow;
    		return;
    	}
    	int mid=(lenow+rinow)>>1;cnt=0;
    	for(int i=lenow;i<=mid;++i)
    	{
    		for(int j=0;j<(int)G[i].size();++j)
    		{//开始扫描线处理修改以及询问
    			int tmp=G[i][j];
    			if(e[tmp].knd==2)
    			{
    				++cnt;
    				et[cnt].at=dfn[e[tmp].u];et[cnt].le=dfn[e[tmp].v];et[cnt].ri=dfn[e[tmp].v]+siz[e[tmp].v]-1;et[cnt].d=1;et[cnt].knd=1;
    				++cnt;
    				et[cnt].at=dfn[e[tmp].u]+siz[e[tmp].u];et[cnt].le=dfn[e[tmp].v];et[cnt].ri=dfn[e[tmp].v]+siz[e[tmp].v]-1;et[cnt].d=-1;et[cnt].knd=1;
    			} 
    			else
    			{
    				++cnt;
    				et[cnt].at=1;et[cnt].le=dfn[e[tmp].v];et[cnt].ri=dfn[e[tmp].v]+siz[e[tmp].v]-1;et[cnt].d=1;et[cnt].knd=1;
    				++cnt;
    				et[cnt].at=dfn[e[tmp].ex];et[cnt].le=dfn[e[tmp].v];et[cnt].ri=dfn[e[tmp].v]+siz[e[tmp].v]-1;et[cnt].d=-1;et[cnt].knd=1;
    				++cnt;
    				et[cnt].at=dfn[e[tmp].v];et[cnt].le=dfn[e[tmp].ex]+siz[e[tmp].ex];et[cnt].ri=n;et[cnt].d=1;et[cnt].knd=1;
    				++cnt;
    				et[cnt].at=dfn[e[tmp].v]+siz[e[tmp].v];et[cnt].le=dfn[e[tmp].ex]+siz[e[tmp].ex];et[cnt].ri=n;et[cnt].d=-1;et[cnt].knd=1;
    			}
    		}
    	}
    	for(int i=le;i<=ri;++i)
    	{
    		vis[i]=0;
    		++cnt;
    		et[cnt].at=dfn[que[i].u];et[cnt].le=dfn[que[i].v];et[cnt].ri=i;et[cnt].knd=2;
    	}
    	sort(et+1,et+cnt+1);
    	for(int i=1;i<=cnt;++i)
    	{//由于是区间修改 单点查询 所以我就使用了树状数组
    		if(et[i].knd==1)
    		update(et[i].le,et[i].d),update(et[i].ri+1,-et[i].d);
    		else
    		{
    			int tmp=query(et[i].le);
    			vis[et[i].ri]+=tmp;
    		}
    	}
    	int cnta=0,cntb=0;
    	for(int i=le;i<=ri;++i)
    	{
    		if(vis[i]>=que[i].k) cdy[++cnta]=que[i];
    		else 
    		{
    			que[i].k-=vis[i];
    			wzy[++cntb]=que[i];
    		}
    	}
    	for(int i=le;i<=le+cnta-1;++i) que[i]=cdy[i-le+1];
    	for(int i=le+cnta;i<=ri;++i) que[i]=wzy[i-le-cnta+1];
    	solve(lenow,mid,le,le+cnta-1);solve(mid+1,rinow,le+cnta,ri);
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1,x,y;i<n;++i)
    	{
    		scanf("%d%d",&x,&y);
    		
    		add(x,y);add(y,x);
    	}
    	dfs(1,0);
    	for(int i=1;i<=m;++i)
    	{
    		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    		if(dfn[e[i].u]>dfn[e[i].v]) swap(e[i].u,e[i].v);
    		res[i]=e[i].w;
    		int tmp=LCA(e[i].u,e[i].v);
    		if(tmp!=e[i].u&&tmp!=e[i].v) e[i].knd=2;
    		else
    		{
    			int now=e[i].v;
    			for(int j=20;j>=0;--j)
    			if(dep[fa[now][j]]>=dep[e[i].u]+1) now=fa[now][j];
    			e[i].ex=now;e[i].knd=1;//判断带权路径的类型 同时求出第一类路径的第一个点z
    		} 
    	}
    	sort(res+1,res+m+1);tot=unique(res+1,res+m+1)-res-1;
    	for(int i=1;i<=m;++i)
    	{//离散化 同时使用vector记录每一个权值对应的路径 便于接下来的整体二分
    		e[i].w=lower_bound(res+1,res+tot+1,e[i].w)-res;
    		G[e[i].w].push_back(i);
    	}
    	for(int i=1;i<=q;++i)
    	{
    		scanf("%d%d%d",&que[i].u,&que[i].v,&que[i].k);
    		if(dfn[que[i].u]>dfn[que[i].v]) swap(que[i].u,que[i].v);
    		que[i].id=i;
    	}
    	solve(1,tot,1,q);
    	sort(que+1,que+q+1);
    	for(int i=1;i<=q;++i) printf("%d\n",res[que[i].ans]);
    	return 0;
    }
    
  • 相关阅读:
    Amoeba for MySQL读写分离配置
    基于amoeba实现mysql数据库的读写分离/负载均衡
    Amoeba实现mysql主从读写分离
    OpenERP 的XML-RPC的轻度体验+many2many,one2many,many2one创建方式
    openerp用wizard导入excel数据
    OpenERP在product中增加外部网络链接图片
    OpenERP7.0安装后提示“not supported" ,如何去掉此提示
    windows命令行设置IP与DNS
    ubuntu开机自动启动xampp/lampp的两种方法
    [Python]网络爬虫(五):urllib2的使用细节与抓站技巧
  • 原文地址:https://www.cnblogs.com/tcswuzb/p/14358022.html
Copyright © 2020-2023  润新知