Description
这题的转化真是妙啊。
对于一条路径x-y,(设dfn[x]<dfn[y]),当x不是 x和y的 lca时, 如果 x-y 是 x1-y1的子路径 ,那么 要求
in[x]<=dfn[x1]<=out[x] && in[y]<=dfn[y1]<=out[y]
如果x是x和y的lca时,设w为x->y的路径上x的第一个儿子, 那么在w上面的点以及不在x->某个点的子树的点都是有可能的,画一下发现如果 x-y 是 x1-y1的子路径,那么要求
(1<=dfn[x1]<=in[w] && in[y]<=dfn[y1]<=out[y]) || (in[y]<=dfn[x1]<=out[y] && in[w]+1<=dfn[y]<=n)
发现一个盘子可以被拆分成一个矩形或者是两个互不相交的矩形,那么问题就变成了,给出一堆点,求覆盖它的矩形权值第k小的是谁,这样就可以用整体二分加扫描线解决了,具体如下。
先对点的x进行排序,对于矩形按照权值排序,
整体二分的过程中,对于矩形,将上下边界提取出来,排序,枚举当前区间的每一个点,将矩形的边界更新到当前的x,如果一个点被覆盖的次数>=k了,说明答案肯定肯定更小,放到左边递归,如果不满足,就把它的k减去当前的覆盖次数,放到右边递归即可。
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 200005
#define mid ((nl+nr)>>1)
using namespace std;
struct nod1
{
int x1,y1,x2,y2,val;
nod1(int a,int b,int c,int d,int e)
{
x1=a;
y1=b;
x2=c;
y2=d;
val=e;
}
nod1(){}
}a[maxn];
struct nod2
{
int x,y,k,id;
nod2(int a,int b,int c,int d)
{
x=a;
y=b;
k=c;
id=d;
}
nod2(){}
}b[maxn],temp1[maxn],temp3[maxn];
struct nod3
{
int l,r,x,f;
nod3(int a,int b,int c,int d)
{
l=a;
r=b;
x=c;
f=d;
}
nod3(){}
}temp2[maxn];
bool operator<(nod1 a,nod1 b)
{
return a.val<b.val;
}
bool operator<(nod2 a,nod2 b)
{
return a.x<b.x;
}
bool operator<(nod3 a,nod3 b)
{
return a.x<b.x;
}
int n,m,q,tot,sz;
int head[maxn],nex[maxn<<1],to[maxn<<1];
void add1(int x,int y)
{
to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int anc[maxn][20],dep[maxn],dfn[maxn],out[maxn],ans[maxn];
void dfs(int now,int fa)
{
dfn[now]=++sz;
for(int i=1;(1<<i)<=dep[now];i++)
anc[now][i]=(anc[anc[now][i-1]][i-1]);
for(int i=head[now];i;i=nex[i])
{
if(to[i]!=fa)
{
anc[to[i]][0]=now;
dep[to[i]]=dep[now]+1;
dfs(to[i],now);
}
}
out[now]=sz;
}
int lca(int p,int q)
{
if(dep[p]<dep[q]) swap(p,q);
int d=(dep[p]-dep[q]);
for(int i=0;i<=18;i++)
if((1<<i)&d)
p=anc[p][i];
if(p==q) return p;
for(int i=18;i>=0;i--)
if(anc[p][i]!=anc[q][i])
p=anc[p][i],q=anc[q][i];
return anc[p][0];
}
int dat[maxn];
int lowbit(int now)
{
return (now&(-now));
}
void add(int pos,int val)
{
for(int i=pos;i<=n;i+=lowbit(i))
dat[i]+=val;
}
int query(int pos)
{
int res=0;
for(int i=pos;i>=1;i-=lowbit(i))
res+=dat[i];
return res;
}
int lca2(int p,int q)
{
for(int i=18;i>=0;i--)
if(dep[anc[p][i]]>dep[q])
p=anc[p][i];
return p;
}
void solve(int nl,int nr,int ql,int qr)
{
// cerr<<"Its "<<nl<<" "<<nr<<" "<<ql<<" "<<qr<<endl;
if(ql>qr) return;
if(nl==nr)
{
for(int i=ql;i<=qr;i++)
ans[b[i].id]=a[nl].val;
return;
}
int top=0;
for(int i=nl;i<=mid;i++)
{
temp2[++top]=nod3(a[i].y1,a[i].y2,a[i].x1,1);
temp2[++top]=nod3(a[i].y1,a[i].y2,a[i].x2+1,-1);
}
sort(temp2+1,temp2+1+top);
// for(int i=1;i<=top;i++)
// cerr<<temp2[i].l<<" "<<temp2[i].r<<" "<<temp2[i].x<<" "<<temp2[i].f<<endl;
int s=1,top1=0,top2=0;
for(int i=ql;i<=qr;i++)
{
while(s<=top && temp2[s].x<=b[i].x)
add(temp2[s].l,temp2[s].f),add(temp2[s].r+1,-temp2[s].f),s++;
int temp=query(b[i].y);
if(temp>=b[i].k)
temp1[++top1]=b[i];
else
temp3[++top2]=b[i],temp3[top2].k-=temp;
}
while(s<=top)
add(temp2[s].l,temp2[s].f),add(temp2[s].r+1,-temp2[s].f),s++;
s=ql;
for(int i=1;i<=top1;i++)
b[s++]=temp1[i];
int t=s-1;
for(int i=1;i<=top2;i++)
b[s++]=temp3[i];
solve(nl,mid,ql,t);
solve(mid+1,nr,t+1,qr);
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add1(x,y); add1(y,x);
}
dfs(1,-1);
tot=0;
for(int i=1;i<=m;i++)
{
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
if(dfn[x]>dfn[y]) swap(x,y);
int l=lca(x,y);
// cerr<<"its "<<l<<endl;
if(l!=x)
{
a[++tot]=nod1(dfn[x],dfn[y],out[x],out[y],v);
}
else
{
int w=lca2(y,x);
// cerr<<"its "<<w<<endl;
a[++tot]=nod1(1,dfn[y],dfn[w]-1,out[y],v);
if(out[w]+1<=n)
{
a[++tot]=nod1(dfn[y],out[w]+1,out[y],n,v);
}
}
}
// cerr<<"+1"<<endl;
for(int i=1;i<=q;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z); if(dfn[x]>dfn[y]) swap(x,y);
b[i]=nod2(dfn[x],dfn[y],z,i);
}
// cerr<<"+2"<<endl;
sort(a+1,a+1+tot);
sort(b+1,b+1+q);
// for(int i=1;i<=tot;i++)
// {
// cerr<<a[i].x1<<" "<<a[i].y1<<" "<<a[i].x2<<" "<<a[i].y2<<" "<<a[i].val<<endl;
// }
solve(1,tot,1,q);
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
}


618

被折叠的 条评论
为什么被折叠?



