luogu P4183 Cow at Large P (暴力吊打點分治)(內有時間復雜度證明)
題面
貝茜被農民們逼進了一個偏僻的農場。農場可視為一棵有N個結點的樹,結點分別編號為 1,2,…,N 。每個葉子結點都是出入口。開始時,每個出入口都可以放一個農民(也可以不放)。每個時刻,貝茜和農民都可以移動到相鄰的一個結點。如果某一時刻農民與貝茜相遇了(在邊上或點上均算),則貝茜將被抓住。抓捕過程中,農民們與貝茜均知道對方在哪個結點。
請問:對於結點 i\((1\leq i\leq N)\),如果開始時貝茜在該結點,最少有多少農民,她才會被抓住。
分析
先考慮問題的簡化版,對於給定的起點s求出答案
設dist(x,y)表示x,y兩點之間的距離
我們發現,對於某一個節點x,若$dist(s,x) \geq \min {dist(x,u) } $ 其中u為x的子樹中的葉子節點,那麽從x走到s的時
候就會被抓住.$\min {dist(x,u) } $可以通過一次BFS預處理出來
顯然奶牛越早被抓住更好,從節點s出發DFS,只要當前節點x滿足上述條件,則ans++,並不訪問x子樹中的節點。因為如果在x點被抓住了,它就不會到比x深度更大的節點去了
這樣的復雜度顯然是\(O(n^2)\)
有一個玄學優化,可以通過此題,方法如下
1.DP一遍求出每個點到最近的葉子節點的距離dp[x]
2.DFS,把鏈縮成一條邊,因為可以發現不管在鏈的哪裏攔截都是一樣的
3.每次暴力,當dp[x]<=dist(x,root)的時候ans++,return
可以證明把鏈縮掉之後是\(O(n\sqrt{n})\)
由於沒有鏈,我們可以把模型簡化為一個滿k叉樹(k>1),每次暴力DFS,顯然DFS的深度不超過樹的深度的1/2,即\(O(log_kn)\)
訪問的子樹大小為
\[1+k+k^2+...+k^{\frac{1}{2}\log_kn}=\frac{k^{(\frac{1}{2}\log_kn)+1}-1}{k-1}=\frac{k^{\log_kn} \times \sqrt k-1}{k-1}=\frac{k\sqrt{n}-1}{k-1}=\sqrt{n}+\frac{\sqrt{n}-1}{k-1}\]
顯然當k=2最大,為\(2\sqrt{n}-1\),
所以總時間復雜度為\(O(n\sqrt{n})\)
代碼
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define maxn 100005
#define INF 0x3f3f3f3f
using namespace std;
inline void qread(int &x){
x=0;
char c=getchar();
while(c<'0'||c>'9'){
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
}
inline void qprint(int x){
if(x==0){
putchar('0');
return;
}else{
if(x>=10) qprint(x/10);
putchar(x%10+'0');
}
}
int n,k;
struct edge{
int from;
int to;
int next;
int nfrom;
int nto;
int len;
}E[maxn<<1];
int head[maxn];
int deg[maxn];
int sz=1;
void add_edge(int u,int v){
deg[u]++;
deg[v]++;
sz++;
E[sz].from=u;
E[sz].to=v;
E[sz].next=head[u];
head[u]=sz;
}
int dp[maxn];//表示離x最近的葉子節點到x的距離
//不能從一個根開始DFS,因為此時根不定,
//到一個點最近的葉子節點不一定在DFS時的子樹裏,而可能在它上方
void bfs(){
queue<int>q;
for(int i=1;i<=n;i++){
if(deg[i]<=1){
q.push(i);
dp[i]=0;
}else dp[i]=INF;
}
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(dp[y]==INF){
dp[y]=min(dp[y],dp[x]+1);
q.push(y);
}
}
}
}
int s,t,d;
void dfs2(int x,int fa,int deep){
if(fa!=0&°[x]!=2){
s=x;
t=fa;
d=deep;
return;
}
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs2(y,x,deep+1);
E[i].nto=s;
E[i].nfrom=t;
E[i].len=d-deep;
}
}
}
int ans=0;
void dfs3(int x,int fa,int deep){
if(deep>=dp[x]){
ans++;
return;
}
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs3(E[i].nto,E[i].nfrom,deep+E[i].len);
}
}
}
int main(){
int u,v;
qread(n);
for(int i=1;i<n;i++){
qread(u);
qread(v);
add_edge(u,v);
add_edge(v,u);
}
for(int i=1;i<=n;i++) deg[i]/=2;
bfs();
for(int i=1;i<=n;i++){
if(deg[i]!=2) dfs2(i,0,0);
}
for(int i=1;i<=n;i++){
ans=0;
dfs3(i,0,0);
qprint(ans);
putchar('\n');
}
}
luogu P4183 Cow at Large P (暴力吊打點分治)(內有時間復雜度證明)