寢室管理

Description

r 64 有一個好朋友,叫r 128 。r 128 是寄宿生,並且最近被老師叫過去當宿管了。宿
管可不是一件很好做的工作,碰巧r 128 有一個工作上的問題想請r 64 幫忙解決。
r 128 的寢室條件不是很好,所以沒有很多錢來裝修。n間寢室僅由n − 1條雙向道
路連接,而且任意兩間寢室之間都可以互達。最近,r 128 被要求對一條路徑上的所有
寢室進行管理, 這條路徑不會重複經過某個點或某條邊。 但他不記得是哪條路徑了。
他只記得這條路徑上有不少於k個寢室。於是,他想請r 64 幫忙數一下,有多少條這
樣的路徑滿足條件。
嗯…還有一個問題。由於最近有一些熊孩子不準晚上講話很不爽,他們決定修築
一條“情報通道”,如果通道建成,寢室就變成了一個n個點n條邊的無向圖。並且,
經過“情報通道”的路徑也是合法的。r 128 心想:通道建成之前,r 64 還有一個高效
的算法幫我數路徑條數,但是通道建成之後,他還有辦法嗎?對,r 64 手忙腳亂,根
本數不清有多少條路徑。於是他找到了你。

Input

輸入第一行爲三個正整數n, m, k(2 ≤ k ≤ n),代表有n間寢室,
m條邊連接它們(n − 1 ≤ m ≤ n;m = n − 1意味着“情報通道”未被修好;m = n意
味着“情報通道”已被修好),以及題目描述中的k。
接下來m行,每行兩個正整數x, y,代表第x間寢室與第y間寢室之間有一條雙向
邊。

Output

輸出僅包含一個整數,代表經過至少k間寢室的路徑條數。

Sample Input

5 5 2
1 3
2 4
3 5
4 1
5 2

Sample Output

20

Data Constraint

這裏寫圖片描述

Solution

對於樹:點分治(模板)
對於環套樹:
首先找到環上一條邊切掉,變成樹,統計答案
然後沒統計到的必定經過這條邊
沿着一個方向掃描整個環,對於當前掃描到的一個點,統計它的子樹到被切掉那條邊其中一個端點的距離與數據結構中的哪一段長度拼起來,然後再把這個子樹中到那條邊另一個端點的距離加入數據結構,統計就行了

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 101000
#define INF 214748347
#define cl(a) memset(a,0,sizeof(a))
#define ll long long
#define lowbit(a) ((a)&(-a))
using namespace std;
int n,m,k,last[N],next[N*2],to[N*2],tot=1,bz[N],root,size[N],ts,a[N],mx[N],deep[N],s[N],c[N],flag=0,t[N*10];
ll ans;
void putin(int x,int y)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;
}
void groot(int x,int fat)
{
    mx[x]=0;
    size[x]=1;
    for(int i=last[x];i;i=next[i])
    if(i!=flag&&i!=(flag^1))
    {
        int y=to[i];if(y==fat||bz[y]) continue;
        groot(y,x);size[x]+=size[y];
        mx[x]=max(mx[x],size[y]);
    }
    mx[x]=max(mx[x],ts-size[x]);
    if(mx[x]<mx[root]) root=x;
}
void gdeep(int x,int fat)
{
    for(int i=last[x];i;i=next[i])
    if(i!=flag&&i!=(flag^1))
    {
        int y=to[i];if(y==fat||bz[y]) continue;
        deep[y]=deep[x]+1;gdeep(y,x);
    }
    a[++tot]=deep[x];
}
ll calc()
{
    ll ans=0,i=1,j=tot;
    sort(a+1,a+tot+1);
    while(i<j)
    {
        if(a[i]+a[j]-1>=k) ans+=j-i,j--;
        else i++;
    }
    return ans;
}
void dg(int x,int fat)
{
    bz[x]=1;deep[x]=1;
    tot=0;gdeep(x,fat);
    ans+=calc();
    for(int i=last[x];i;i=next[i])
    if(i!=flag&&i!=(flag^1))
    {
        int y=to[i];if(bz[y]) continue;
        tot=0;deep[y]=2;gdeep(y,x);
        ans-=calc();
        ts=size[y];root=0;groot(y,x);
        dg(root,x);
    }
}
void findc(int x,int fat)
{
    s[++s[0]]=x;bz[x]=1;
    for(int i=last[x];i;i=next[i])
    if(i!=fat)
    {
        if(flag) return;
        int y=to[i];
        if(bz[y])
        {
            for(;s[s[0]+1]!=y;s[0]--) c[++c[0]]=s[s[0]];
            flag=i;
            return;
        }
        findc(y,i^1);
    }
    s[0]--;
}
void ins(int x)
{
    for(;x<=n;x+=lowbit(x)) t[x]++;
}
ll get(int x)
{
    ll jy=0;
    for(;x;x-=lowbit(x)) jy+=t[x];
    return jy;
}
void dfs(int qq,int x,int fat,int d)
{
    if(qq==1) ans+=get(n)-get(max(k-d-1,0));
    else ins(d);
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];if(y==fat||s[y]) continue;
        dfs(qq,y,x,d+1);
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    fo(i,1,m)
    {
        int x,y;scanf("%d%d",&x,&y);
        putin(x,y);putin(y,x);
    }
    if(m==n-1)
    {
        mx[0]=INF;root=0;ts=n;groot(1,0);
        dg(root,0);
        printf("%lld\n",ans);
    }
    else
    {
        findc(1,0);cl(bz);
        mx[0]=INF;root=0;ts=n;groot(1,0);
        dg(root,0);
        int jy=0;cl(s);
        fo(i,1,c[0]) s[c[i]]=1;
        fo(i,1,c[0])
        {
            dfs(1,c[i],0,c[0]-jy);
            jy++;
            dfs(2,c[i],0,jy);
        }
        printf("%lld\n",ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章