前言
这是一道考试题
需要一定的idea
构造好后似乎就是裸的点分治了
题目相关
题目大意
这个大意写的很烦,不如看题面
有一棵
n
n
n个点的树
设定一个动点:其每秒可以走到树上相邻的一个节点,若当前它在一个度数为1的节点上,它可以逃出这棵树
定义动点相遇为它们在节点上或边上相遇
对于每个点,求现在在当前点上有个动点想要逃出这棵树,你至少要在多少个叶子节点上放置防守动点才能保证它不会逃出去(如果防守动点碰到动点,那么那个动点就被抓住了)
数据范围
n ≤ 7 ∗ 1 0 4 n\le7*10^4 n≤7∗104
题解
我们可以通过
Θ
(
n
)
\Theta(n)
Θ(n)的树形dp求出离
i
i
i点最近的叶子节点到
i
i
i点的距离
g
i
g_i
gi
可能算上下dp
设
i
i
i号点的度数为
d
i
d_i
di
我们发现对于
d
i
=
1
d_i=1
di=1的点答案一定为
1
1
1(即放一个点在它自己上即可)
那么我们考虑如何统计
d
i
≠
1
d_i\neq1
di̸=1的点
对于每一个根(即选中的那个点),设
d
e
p
i
dep_i
depi为根到
i
i
i点的距离,
f
a
i
fa_i
fai为
i
i
i号点的父亲
每当有一个节点
i
i
i满足
d
e
p
i
≥
g
i
dep_i\ge g_i
depi≥gi并且
d
e
p
f
a
i
<
g
f
a
i
dep_{fa_i}<g_{fa_i}
depfai<gfai,那么当前根的答案加一
然后这个统计大概是
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)的
然后考虑优化
我们发现对于
d
e
p
i
≥
g
i
dep_i\ge g_i
depi≥gi的节点的存在都在联通子树中
每个联通子树贡献
1
1
1的答案(树根贡献)
如果我们可以快速计数就可以获取答案
然后这个时候有一个很巧妙的构造
考场上我并没有想到这个
对于一个
m
m
m个点的子树,其边数是
m
−
1
m-1
m−1条
我们发现对于这个子树
∑
d
i
=
2
∗
(
m
−
1
)
+
1
\sum d_i=2*(m-1)+1
∑di=2∗(m−1)+1
我们又发现
∑
1
=
m
\sum1=m
∑1=m
所以
∑
d
i
=
2
∗
(
m
−
1
)
+
1
∑
d
i
=
2
∗
m
−
1
1
=
2
∗
m
−
∑
d
i
1
=
∑
(
2
−
d
i
)
\begin{aligned} \sum d_i&=2*(m-1)+1\\ \sum d_i&=2*m-1\\ 1&=2*m-\sum d_i\\ 1&=\sum(2-d_i) \end{aligned}
∑di∑di11=2∗(m−1)+1=2∗m−1=2∗m−∑di=∑(2−di)
刚好符合我们想要的
所以我们现在要求的就是:
∑
i
=
1
n
[
d
e
p
i
≥
g
i
]
(
2
−
d
i
)
\sum_{i=1}^n[dep_i\ge g_i](2-d_i)
i=1∑n[depi≥gi](2−di)
我们发现
∑
i
=
1
n
(
2
−
d
i
)
=
∑
i
=
1
n
2
−
∑
i
=
1
n
d
i
=
2
∗
n
−
2
∗
(
n
−
1
)
=
2
\begin{aligned} \sum_{i=1}^n(2-d_i)&=\sum_{i=1}^n2-\sum_{i=1}^nd_i\\ &=2*n-2*(n-1)\\ &=2 \end{aligned}
i=1∑n(2−di)=i=1∑n2−i=1∑ndi=2∗n−2∗(n−1)=2
然后我们要求的就成了
2
−
∑
i
=
1
n
[
d
e
p
i
<
g
i
]
(
2
−
d
i
)
2-\sum_{i=1}^n[dep_i<g_i](2-d_i)
2−i=1∑n[depi<gi](2−di)
设点
u
u
u的答案为
a
n
s
u
ans_u
ansu
a
n
s
u
=
2
−
∑
v
=
1
n
[
d
i
s
(
u
,
v
)
<
g
v
]
(
2
−
d
v
)
ans_u=2-\sum_{v=1}^n[dis(u,v)<g_v](2-d_v)
ansu=2−v=1∑n[dis(u,v)<gv](2−dv)
然后就可以使用点分治
我们点分的时候每次计算经过当前分治重心的路径点对
(
u
,
v
)
(u,v)
(u,v)
设点
i
i
i与分治重心的距离为
p
i
p_i
pi
d
i
s
(
u
,
v
)
=
p
u
+
p
v
dis(u,v)=p_u+p_v
dis(u,v)=pu+pv
然后
d
i
s
(
u
,
v
)
<
g
v
⇔
p
u
+
p
v
<
g
v
⇔
p
u
<
g
v
−
p
v
dis(u,v)<g_v\Leftrightarrow p_u+p_v<g_v\Leftrightarrow p_u<g_v-p_v
dis(u,v)<gv⇔pu+pv<gv⇔pu<gv−pv
用树状数组维护即可
复杂度
Θ
(
n
l
o
g
2
n
)
\Theta(nlog^2n)
Θ(nlog2n)
代码
贴上AC代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cmath>
namespace fast_IO
{
const int IN_LEN=10000000,OUT_LEN=10000000;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
typedef long long LL;
#define rg register
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
const int maxn=70001,maxm=140002,INF=0x7f7f7f7f;
int n,d[maxn];
int head[maxn],nxt[maxm],tow[maxm],tmp=1;
int fi[maxn];
inline void addb(const int u,const int v)
{
tmp++;
nxt[tmp]=head[u];
head[u]=tmp;
tow[tmp]=v;
}
void dfs1(const int u,const int fa)
{
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa)continue;
dfs1(v,u);
mind(fi[u],fi[v]+1);
}
if(fi[u]==INF)fi[u]=0;
}
void dfs2(const int u,const int fa)
{
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa)continue;
mind(fi[v],fi[u]+1);
dfs2(v,u);
}
}
bool Hash[maxn];int son[maxn],minn,root,size;
int ans[maxn];
void getroot(const int u,const int fa)
{
son[u]=1;
int maxx=0;
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa||Hash[v])continue;
getroot(v,u);
son[u]+=son[v];
maxd(maxx,son[v]);
}
maxd(maxx,size-son[u]);
if(maxx<minn)minn=maxx,root=u;
}
int Q[maxn],tot,dis[maxn];
void dfs(const int u,const int fa)
{
Q[++tot]=u;
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa||Hash[v])continue;
dis[v]=dis[u]+1,dfs(v,u);
}
}
int tree[maxn];
inline int lowbit(const int x){return x&-x;}
inline void add(int whe,const int man)
{
while(whe<=n)
{
tree[whe]+=man;
whe+=lowbit(whe);
}
}
inline int qz(int whe)
{
rg int res=0;
while(whe)
{
res+=tree[whe];
whe^=lowbit(whe);
}
return res;
}
void calc(const int u,const int sign,const int G)
{
tot=0,dis[u]=G;
dfs(u,u);
for(rg int i=1;i<=tot;i++)
{
const int v=Q[i];
add(n-fi[v]+dis[v],2-d[v]);
}
for(rg int i=1;i<=tot;i++)
{
const int v=Q[i];
ans[v]+=sign*qz(n-dis[v]-1);
}
for(rg int i=1;i<=tot;i++)
{
const int v=Q[i];
add(n-fi[v]+dis[v],d[v]-2);
}
}
void solve(const int u,const int SIZE,const int SON)
{
Hash[u]=1;
calc(u,-1,0);
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(Hash[v])continue;
calc(v,1,1);
minn=INF,size=son[v];
if(size>SON)size=SIZE-SON;
getroot(v,u),solve(root,size,son[root]);
}
}
int main()
{
memset(fi,0x7f,sizeof(fi));
read(n);
for(rg int i=1;i<n;i++)
{
int u,v;read(u),read(v);
addb(u,v),addb(v,u);
d[u]++,d[v]++;
}
int RT=0;
for(rg int i=1;i<=n;i++)if(d[i]>1)RT=i;
dfs1(RT,RT);
dfs2(RT,RT);
for(rg int i=1;i<=n;i++)ans[i]=2;
minn=INF,size=n,getroot(1,1),solve(root,size,son[root]);
for(rg int i=1;i<=n;i++)print(d[i]==1?1:ans[i]),putchar('\n');
return flush(),0;
}
总结
构造很巧妙,一个很好的idea
然后点分治比较裸,很清真