这是一道考试题
需要一定的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
#include
#include
#include
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
然后点分治比较裸,很清真