【题目】
原题地址
大意:求有多少个树上点对
(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
考虑对一条路径,如果我们取到的最小值在根到上,那么就是在线段树中查找
depy
d
e
p
y
的最大值使得柿子最小(不用考虑
min
m
i
n
在根到
y
y
路径上,因为如果在根到
y
y
路径上,显然柿子的值大于0)
如果在根到
y
y
路径上,那么我们要使柿子的值尽量小,就是让尽量大,这个就是我们要在线段树上维护的东西。
做完一个节点后将这个点从线段树上删去即可。
接下来是正解的做法:
我们设一个函数
f(x,y)
f
(
x
,
y
)
表示
x
x
到的路线上
[经过边数−(w,w+1)同时存在的对数]
[
经
过
边
数
−
(
w
,
w
+
1
)
同
时
存
在
的
对
数
]
,显然如果一条路线是合法的,它的f为0.
分别考虑每条树边和数对对
f
f
的贡献,则每条路径的贡献可以在二维平面上表示为2个或4个矩形。
具体是这样的:对于一条路径,如果
lca(u,v)
l
c
a
(
u
,
v
)
不是这两个点,则贡献的路径是在两个点的子树中分别取一个点。
否则,设
u
u
是的祖先,
w
w
是到
v
v
路径上的第一个点,则贡献的路径是在的子树中取一个点,再在不是
w
w
的子树中取一个点。
上面的每一个”取”就对应序上的一段或两段区间,取的两个点分别作为
x
x
和。
注意我们的每个矩形,我们要将它的
x
x
和交换后作为一个新统计答案矩形放进去,这样才能统计答案。
然后就可以用扫描线+线段树来做了,每次查询0的个数即可(最小值)。
最后的答案是我们上面得出的答案
ans+n2
a
n
s
+
n
2
实现参照代码,原来写的直接丢矩形,常数极大没有过。优化扫描线以后勉强能在BZOJ上过去,修改前和修改后的部分在代码中都有体现。
【参考代码】
#include<bits/stdc++.h>
#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<B.opt;
return A.x<B.x;
}
struct 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]<mi[rs])
mi[x]=mi[ls],minum[x]=minum[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]<dep[y]) swap(x,y);
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;i<n;++i)
{
int u=read(),v=read();
add(u,v);
}
dfs(dep[1]=1,0);
dfs2(1);
for(int i=1;i<n;++i)
{
int 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;
}
【总结】
这个转换函数的思想很好用,事实上对于某些树上问题,我们也可以将可行点域映射到二维平面上去解决问题。
然后就是这题的常数问题,很多时候重复或无用的东西应当优化掉,不然可能会被卡成**