bzoj3648 寢室管理 樹分治

題目大意:
給一顆基環樹,求所有長度大於等於K的路徑。

題目分析:
如果沒有環,只有樹的話,只要裸上樹分治就可以了。
有環的話我們就先把環上的一條邊去掉,然後做樹分治。
這樣我們就統計出了所有不經過這條邊的路徑樹,還剩下所有經過這條邊的路徑數。
我們此時處理出以環上所有點位根的子樹信息,並將其合併。
然後每次去掉環上一個點的信息,再用這個點的子樹信息與環上其他點的子樹信息更新答案(通過剛纔刪掉那條邊的)。

代碼如下:

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#define N 120000
using namespace std;
typedef long long LL;
inline int lowbit(int x) { return x&-x; }
int n,m,K,sum,root,rtf;
int fir[N],nes[N<<1],v[N<<1],tot=1;
int fw[N],tim[N],fa[N],son[N],sz[N],sta[N],top;
int Time_Stamp,Cut;
bool mark[N],vis[N];
vector<int> ring;
LL ans;
void edge(int x,int y)
{
    v[++tot]=y;
    nes[tot]=fir[x];
    fir[x]=tot;
    return;
}
#define edge(x,y) edge(x,y),edge(y,x)
void update(int x,int v=1)
{
    for(;x;x-=lowbit(x))
    {
        if(tim[x]!=Time_Stamp)
            tim[x]=Time_Stamp,fw[x]=0;
        fw[x]+=v;
    }
    return;
}
int query(int x)
{
    int ans=0;
    if(x<=0) x=1;
    for(;x<=n;x+=lowbit(x))
    {
        if(tim[x]!=Time_Stamp)
            tim[x]=Time_Stamp,fw[x]=0;
        ans+=fw[x];
    }
    return ans;
}
void DFS(int c)
{
    mark[c]=true;
    for(int t=fir[c];t;t=nes[t])
    {
        if(v[t]==fa[c] || t==Cut || t==(Cut^1)) continue;
        if(mark[v[t]])
        {
            int tmp=c;
            while(tmp!=v[t])
            {
                ring.push_back(tmp);
                tmp=fa[tmp];
            }
            ring.push_back(tmp);
            Cut=t;
            continue;
        }
        fa[v[t]]=c;
        DFS(v[t]);
    }
}
void find_focus(int c,int fa)
{
    sz[c]=1; son[c]=0;
    for(int t=fir[c];t;t=nes[t])
    {
        if(v[t]==fa || t==Cut || t==(Cut^1) || vis[v[t]]) continue;
        find_focus(v[t],c);
        sz[c]+=sz[v[t]];
        if(sz[v[t]]>son[c]) son[c]=sz[v[t]];
    }
    if(sum-sz[c]>son[c]) son[c]=sum-sz[c];
    if(son[c]<=son[root]) root=c,rtf=fa;
}
void dfs(int c,int fa,int dep)
{
    sta[++top]=dep;
    for(int t=fir[c];t;t=nes[t])
    {
        if(v[t]==fa || vis[v[t]] || t==Cut || t==(Cut^1)) continue;
        dfs(v[t],c,dep+1);
    }
}
void solve(int c)
{
    vis[c]=true;
    Time_Stamp++;
    update(1);
    for(int t=fir[c];t;t=nes[t])
    {
        if(vis[v[t]] || t==Cut || t==(Cut^1)) continue;
        top=0;
        dfs(v[t],0,1);
        for(int i=1;i<=top;i++) ans+=query(K-sta[i]);
        for(int i=1;i<=top;i++) update(sta[i]+1);
    }
    for(int t=fir[c];t;t=nes[t])
    {
        if(vis[v[t]] || t==Cut || t==(Cut^1)) continue;
        root=0; sum=sz[v[t]];
        find_focus(v[t],0);
        solve(root);
        sz[rtf]=sum-sz[root];
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        edge(x,y);
    }
    DFS(1);
    son[0]=sum=n; root=0;
    find_focus(1,0);
    sz[rtf]=sum-sz[root];
    solve(root);
    sum=ring.size();
    memset(vis,0,sizeof(vis));
    for(int i=0;i<sum;i++) vis[ring[i]]=true;
    Time_Stamp++;
    for(int i=0;i<sum;i++)
    {
        top=0;
        dfs(ring[i],0,1);
        for(int j=1;j<=top;j++)
            update(sta[j]+i);
    }
    for(int i=sum-1;i>=0;i--)
    {
        top=0;
        dfs(ring[i],0,1);
        for(int j=1;j<=top;j++)
            update(sta[j]+i,-1);
        for(int j=1;j<=top;j++)
            ans+=query(K-sta[j]-sum+i+1);
    }
    printf("%lld\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章