[JZOJ3978] 寝室管理

Description

r 128 的寝室条件不是很好,所以没有很多钱来装修。n间寝室仅由n − 1条双向道路连接,而且任意两间寝室之间都可以互达。最近,r 128 被要求对一条路径上的所有寝室进行管理, 这条路径不会重复经过某个点或某条边。 但他不记得是哪条路径了。他只记得这条路径上有不少于k个寝室
于是,他想请r 64 帮忙数一下,有多少条这样的路径满足条件。
嗯…还有一个问题。由于最近有一些熊孩子不准晚上讲话很不爽,他们决定修筑一条“情报通道”,如果通道建成,寝室就变成了一个n个点n条边的无向图。并且,经过“情报通道”的路径也是合法的。

这里写图片描述

Solution

如果这是一棵树,那么就是点分治经典问题。

对于每个分治中心按顺序扫子树,记录距离数组,暴力改后缀和,注意更新数组的时候只需要扫这个子树大小那么多,保证了每层复杂度都是O(N)的

那么环套树的情况,先断掉环上一条边跑点分治,那么剩下的路径都必须过这条边。

那么在环上从这条边的一个端点开始按顺序扫,对于一个环上一个点遍历它的子树,在树状数组中计算答案。然后加入树状数组中。
总的复杂度O(NlogN)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define N 200005
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
using namespace std;
int n,m,lim,nt[2*N],dt[2*N],fs[N],fx[2*N],a[N],id,sz[N],m1,ls,mx,mw;
LL dis[N],d1[N],ans,c[N];
bool bz[2*N],b[N];
void link(int x,int y)
{
    nt[++m1]=fs[x];
    dt[fs[x]=m1]=y;
}
void dfs(int k,int fa)
{
    b[k]=1;
    for(int i=fs[k];i;i=nt[i])
    {
        if(fx[i]==fa) continue;
        int p=dt[i];
        if(b[p])
        {
            bz[i]=bz[fx[i]]=1,id=i,ls=p;
            a[++a[0]]=k;
            return;
        }
        else dfs(p,i);
        if(ls) 
        {
            a[++a[0]]=k;
            if(ls==k) ls=0;
            return;
        }
    }
}
void fd(int k,int fa,int sum)
{
    int s=0;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(!b[p]&&!bz[i]&&p!=fa)
        {
            fd(p,k,sum);
            s=max(s,sz[p]);
        }
    }
    if(max(sum-sz[k],s)<mx) mx=max(sum-sz[k],s),mw=k;
}
void dg(int k,int fa)
{
    sz[k]=1;
    for(int i=fs[k];i;i=nt[i]) 
        if(!b[dt[i]]&&!bz[i]&&dt[i]!=fa) dg(dt[i],k),sz[k]+=sz[dt[i]];
}
void fds(int k,int fa,int s)
{
    ans+=dis[max(0,lim-s)];
    d1[s]++;
    for(int i=fs[k];i;i=nt[i])
        if(!b[dt[i]]&&!bz[i]&&dt[i]!=fa) fds(dt[i],k,s+1);
}
void doit(int k,int si)
{
    b[k]=1;
    dg(k,0);
    dis[0]=1;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(!b[p]&&!bz[i])
        {
            fds(p,k,1);
            fod(j,sz[p],0) 
            {
                d1[j]+=d1[j+1];
                d1[j+1]=0;
                dis[j]+=d1[j];
            }
            d1[0]=0;
        }
    }
    fo(i,0,sz[k]) dis[i]=0;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(!b[p]&&!bz[i])
        {
            mx=n;
            mw=p;
            fd(p,k,sz[p]);
            doit(mw,sz[p]);
        }
    }
}
int lowbit(int k)
{
    return (k&(-k));
}
void put(int k)
{
    while(k<=n+1) c[k]++,k+=lowbit(k);
}
LL gans(int k)
{
    LL s=0;
    while(k) s+=c[k],k-=lowbit(k);
    return s;
}
void ins(int k,int fa,int s)
{
    put(s+1);
    for(int i=fs[k];i;i=nt[i]) 
        if(!b[dt[i]]&&dt[i]!=fa) ins(dt[i],k,s+1);
}
LL get(int k,int fa,int s)
{
    ans+=gans(n+1)-gans(max(0,lim-s));
    for(int i=fs[k];i;i=nt[i]) 
        if(!b[dt[i]]&&dt[i]!=fa) get(dt[i],k,s+1);
}
int main()
{
    cin>>n>>m>>lim;
    lim--;
    fo(i,1,m) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y),link(y,x);
        fx[m1]=m1-1,fx[m1-1]=m1;
    }
    if(m==n) dfs(1,0);
    memset(b,0,sizeof(b));
    mx=n;
    dg(1,0);
    fd(1,0,n);
    doit(mw,n);
    memset(b,0,sizeof(b));
    bz[id]=bz[fx[id]]=0;
    fo(i,1,a[0]) b[a[i]]=1;
    ins(a[1],0,1);
    fo(i,2,a[0])
    {
        get(a[i],0,a[0]-i);
        ins(a[i],0,i);
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值