1. 程式人生 > >luogu P4183 Cow at Large P (暴力吊打點分治)(內有時間復雜度證明)

luogu P4183 Cow at Large P (暴力吊打點分治)(內有時間復雜度證明)

葉子 pri for max getch 才會 != 模型 mes

題面

貝茜被農民們逼進了一個偏僻的農場。農場可視為一棵有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&&deg[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 (暴力吊打點分治)(內有時間復雜度證明)