【题目】
原题地址
大意:求有多少个树上点对 (u,v) ( u , v ) 满足这条路径上经过的所有点排序后恰好为连续的一段,这里 (u,v)和(v,u) ( u , v ) 和 ( v , u ) 算作同一情况
【解题思路】
这道题目的思想挺妙的。
我们很容易可以得到一个 O(nlog2n) O ( n l o g 2 n ) 的常数比较大的做法,但很可惜这题过不了。
简单说一下:考虑一条序列上我们如何处理这个问题,发现 [l,r] [ l , r ] 满足条件的充要条件是 max[l,r]−min[l,r]=r−l+1 m a x [ l , r ] − m i n [ l , r ] = r − l + 1 ,同时若将字母及常数全部移到左边,柿子的值恒>=0。
然后就可以通过维护最小值及个数来得到答案。
对于这道题我们考虑将序列问题应用到树上,采用点分治的手段。
对于到当前重心,我们记录每个点的 dep d e p 和路径上的 min m i n ,路径上的 max m a x 。
这里的柿子变成 max−min=depx+depy−1 m a x − m i n = d e p x + d e p y − 1 可化为 max−min−depx−depy+1>=0 m a x − m i n − d e p x − d e p y + 1 >= 0
接着讲每个点按照 min m i n 排序,并再开一个数组将点按 max m a x 排序。
接下来将每个 min m i n 插入线段树,然后按 max m a x 从大到小在线段树上查询,这样可以保证我们得到一个最大值。设当前考虑点为 x x
考虑对一条路径,如果我们取到的最小值在根到 x x 上,那么就是在线段树中查找 depy d e p y 的最大值使得柿子最小(不用考虑 min m i n 在根到 y y 路径上,因为如果 min m i n 在根到 y y 路径上,显然柿子的值大于0)
如果 min m i n 在根到 y y 路径上,那么我们要使柿子的值尽量小,就是让 min+depy m i n + d e p y 尽量大,这个就是我们要在线段树上维护的东西。
做完一个节点后将这个点从线段树上删去即可。
接下来是正解的做法:
我们设一个函数 f(x,y) f ( x , y ) 表示 x x 到 y y 的路线上 [经过边数−(w,w+1)同时存在的对数] [ 经 过 边 数 − ( w , w + 1 ) 同 时 存 在 的 对 数 ] ,显然如果一条路线是合法的,它的f为0.
分别考虑每条树边和数对对 f f 的贡献,则每条路径的贡献可以在二维平面上表示为2个或4个矩形。
具体是这样的:对于一条路径 (u,v) ( u , v ) ,如果 lca(u,v) l c a ( u , v ) 不是这两个点,则贡献的路径是在两个点的子树中分别取一个点。
否则,设 u u 是 v v 的祖先, w w 是 u u 到 v v 路径上的第一个点,则贡献的路径是在 v v 的子树中取一个点,再在不是 w w 的子树中取一个点。
上面的每一个”取”就对应 dfs d f s 序上的一段或两段区间,取的两个点分别作为 x x 和 y y 。
注意我们的每个矩形,我们要将它的 x x 和 y y 交换后作为一个新统计答案矩形放进去,这样才能统计答案。
然后就可以用扫描线+线段树来做了,每次查询0的个数即可(最小值)。
最后的答案是我们上面得出的答案 ans+n2 a n s + n 2
实现参照代码,原来写的直接丢矩形,常数极大没有过。优化扫描线以后勉强能在BZOJ上过去,修改前和修改后的部分在代码中都有体现。
【参考代码】
#include
#define ls (x<<1)
#define rs (x<<1|1)
using namespace std;
typedef long long LL;
const int INF=1e9;
const int N=3e5+10;
const int M=N*4;
int n,cnt,ind,tot;
int fc[25],head[N],dep[N],beg[N],en[N];
int fa[24][N];
LL ans;
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) {ret=(ret<<1)+(ret<<3)+(c^48);c=getchar();}
return f?ret:-ret;
}
struct Tway
{
int v,nex;
};
Tway e[N<<1];
void add(int u,int v)
{
e[++tot]=(Tway){v,head[u]};head[u]=tot;
e[++tot]=(Tway){u,head[v]};head[v]=tot;
}
struct Tmat
{
int x,y,dy,ad/*,opt*/;
Tmat(){}
// Tmat(int xx,int yy,int dyy,int da,int tt){x=xx;y=yy;dy=dyy;ad=da;opt=tt;}
Tmat(int xx,int yy,int dyy,int da){x=xx;y=yy;dy=dyy;ad=da;}
};
Tmat a[N<<4];
bool cmp(Tmat A,Tmat B)
{
// if(A.x==B.x)
// return A.opt
return A.xstruct Segment
{
int mi[M],minum[M],tar[M];
void pushup(int x)
{
if(mi[ls]==mi[rs])
mi[x]=mi[ls],minum[x]=minum[ls]+minum[rs];
else
if(mi[ls]else
mi[x]=mi[rs],minum[x]=minum[rs];
}
void pushdown(int x)
{
mi[ls]+=tar[x];mi[rs]+=tar[x];
tar[ls]+=tar[x];tar[rs]+=tar[x];
tar[x]=0;
}
void build(int x,int l,int r)
{
if(l==r)
{
mi[x]=0;minum[x]=1;tar[x]=0;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int L,int R,int v)
{
if(L<=l && r<=R)
{
mi[x]+=v;tar[x]+=v;
return;
}
pushdown(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,v);
if(R>mid) update(rs,mid+1,r,L,R,v);
pushup(x);
}
}tr;
void dfs(int x,int f)
{
beg[x]=++ind;
for(int i=1;i<23;++i)
fa[i][x]=fa[i-1][fa[i-1][x]];
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==f)
continue;
fa[0][v]=x;dep[v]=dep[x]+1;
dfs(v,x);
}
en[x]=ind;
}
int lca(int x,int y)
{
if(dep[x]for(int i=0,t=dep[x]-dep[y];t;++i)
if(t&fc[i])
x=fa[i][x],t^=fc[i];
for(int i=22;~i;--i)
if(fa[i][x]^fa[i][y])
x=fa[i][x],y=fa[i][y];
return x==y?x:fa[0][x];
}
void dfs2(int x)
{
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa[0][x])
continue;
/* a[++cnt]=Tmat(beg[v],1,n,1,1);a[++cnt]=Tmat(en[v],1,n,-1,2);
a[++cnt]=Tmat(beg[v],beg[v],en[v],-1,1);a[++cnt]=Tmat(en[v],beg[v],en[v],1,2);
a[++cnt]=Tmat(1,beg[v],en[v],1,1);a[++cnt]=Tmat(n,beg[v],en[v],-1,2);
a[++cnt]=Tmat(beg[v],beg[v],en[v],-1,1);a[++cnt]=Tmat(en[v],beg[v],en[v],1,2);*/
a[++cnt]=Tmat(beg[v],1,n,1);a[++cnt]=Tmat(beg[v],beg[v],en[v],-1);
if(en[v]+1<=n) a[++cnt]=Tmat(en[v]+1,1,n,-1),a[++cnt]=Tmat(en[v]+1,beg[v],en[v],2);
a[++cnt]=Tmat(1,beg[v],en[v],1);a[++cnt]=Tmat(beg[v],beg[v],en[v],-1);
dfs2(v);
}
}
void init()
{
fc[0]=1; for(int i=1;i<=23;++i) fc[i]=fc[i-1]<<1;
n=read();
for(int i=1;iint u=read(),v=read();
add(u,v);
}
dfs(dep[1]=1,0);
dfs2(1);
for(int i=1;iint f=lca(i,i+1);
if(f!=i && f!=i+1)
{
/* a[++cnt]=Tmat(beg[i],beg[i+1],en[i+1],-1,1);a[++cnt]=Tmat(en[i],beg[i+1],en[i+1],1,2);
a[++cnt]=Tmat(beg[i+1],beg[i],en[i],-1,1);a[++cnt]=Tmat(en[i+1],beg[i],en[i],1,2);*/
a[++cnt]=Tmat(beg[i],beg[i+1],en[i+1],-1);a[++cnt]=Tmat(beg[i+1],beg[i],en[i],-1);
if(en[i]+1<=n) a[++cnt]=Tmat(en[i]+1,beg[i+1],en[i+1],1);
if(en[i+1]+1<=n) a[++cnt]=Tmat(en[i+1]+1,beg[i],en[i],1);
}
else
{
int x=(f==i?i:i+1),y=(f==i?i+1:i),z=y;
for(int j=0,t=dep[y]-dep[x]-1;t;++j)
if(t&fc[j])
t^=fc[j],z=fa[j][z];
/* a[++cnt]=Tmat(beg[y],1,n,-1,1);a[++cnt]=Tmat(en[y],1,n,1,2);
a[++cnt]=Tmat(beg[y],beg[z],en[z],1,1);a[++cnt]=Tmat(en[y],beg[z],en[z],-1,2);
a[++cnt]=Tmat(1,beg[y],en[y],-1,1);a[++cnt]=Tmat(n,beg[y],en[y],1,2);
a[++cnt]=Tmat(beg[z],beg[y],en[y],1,1);a[++cnt]=Tmat(en[z],beg[y],en[y],-1,2);*/
a[++cnt]=Tmat(beg[y],1,n,-1);a[++cnt]=Tmat(beg[y],beg[z],en[z],1);
if(en[y]+1<=n) a[++cnt]=Tmat(en[y]+1,1,n,1),a[++cnt]=Tmat(en[y]+1,beg[z],en[z],-1);
a[++cnt]=Tmat(1,beg[y],en[y],-1);a[++cnt]=Tmat(beg[z],beg[y],en[y],1);
if(en[z]+1<=n) a[++cnt]=Tmat(en[z]+1,beg[y],en[y],-1);
}
}
}
void solve()
{
tr.build(1,1,n);
sort(a+1,a+cnt+1,cmp);
int nowl=1,nowr=1;
for(int i=1;i<=n;++i)
{
/* if(a[nowl].x^i)
{
if(!tr.mi[1]) ans+=tr.minum[1];
continue;
}
if(a[nowl].opt==1)
tr.update(1,1,n,a[nowl].y,a[nowl].dy,a[nowl].ad);
while(a[nowr+1].x==a[nowl].x && a[nowr+1].opt==1)
++nowr,tr.update(1,1,n,a[nowr].y,a[nowr].dy,a[nowr].ad);
if(!tr.mi[1]) ans+=tr.minum[1];
if(a[nowl].opt==2)
tr.update(1,1,n,a[nowl].y,a[nowl].dy,a[nowl].ad);
while(a[nowr+1].x==a[nowl].x)
++nowr,tr.update(1,1,n,a[nowr].y,a[nowr].dy,a[nowr].ad);*/
if(a[nowl].x^i)
{
if(!tr.mi[1]) ans+=tr.minum[1];
continue;
}
tr.update(1,1,n,a[nowl].y,a[nowl].dy,a[nowl].ad);
while(a[nowr+1].x==a[nowl].x)
++nowr,tr.update(1,1,n,a[nowr].y,a[nowr].dy,a[nowr].ad);
if(!tr.mi[1]) ans+=tr.minum[1];
nowl=nowr+1;nowr=nowl;
}
ans=(ans+n)/2ll;
printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ5392.in","r",stdin);
freopen("BZOJ5392.out","w",stdout);
#endif
init();
solve();
return 0;
}
【总结】
这个转换函数的思想很好用,事实上对于某些树上问题,我们也可以将可行点域映射到二维平面上去解决问题。
然后就是这题的常数问题,很多时候重复或无用的东西应当优化掉,不然可能会被卡成**