BZOJ3648:寝室管理

浅谈树分治:https://www.cnblogs.com/AKMer/p/10014803.html

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3648

基环树分治,结合了点分和边分的思想。

先随便拆掉环上一条边,我们先称它为基边,把它当做树做一遍,跟Free Tour II差不多。

剩下的问题就是如何统计经过基边的路径条数了。我们将基边连着的两个点分别成为\(st\)\(ed\)\(st\)的顺时针方向是\(ed\)\(ed\)的逆时针方向是\(st\)。直接跟边分一样去分别处理\(st\)\(ed\)能延伸出去的路径长度不行,因为这样会在环上产生交集然后统计不合法的路径。所以我们枚举基边以外的边去断开再统计答案,就可以保证统计出来的答案不重不漏了。

然后因为只要做一遍,所以大力树状数组也没关系。

时间复杂度:\(O(nlogn)\)

空间复杂度:\(O(n)\)

代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define low(i) ((i)&(-(i)))

const int maxn=1e5+5;

ll ans;
bool vis[maxn],bo;
int n,m,limit,tot=1,id,mx,rt,N;
int now[maxn],pre[maxn*2],son[maxn*2];
int siz[maxn],f[maxn],g[maxn],depest[maxn],V[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

void add(int a,int b) {
    pre[++tot]=now[a];
    now[a]=tot,son[tot]=b;
}

struct TreeArray {
    int c[maxn];

    void change(int pos,int v) {
        if(pos==n-13)mx++;
        for(int i=pos;i<=n;i+=low(i))
            c[i]+=v;
    }

    int query(int pos) {
        int res=0;
        for(int i=pos;i;i-=low(i))
            res+=c[i];
        return res;
    }
}T;

struct ring {
    int node1,node2;
    int dis[maxn],nxt[maxn],lst[maxn];

    bool find_ring(int fa,int u) {
        vis[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(v!=fa) {
                if(vis[v])id=p>>1,node1=v,node2=u;
                if(vis[v]||find_ring(u,v)) {
                    nxt[u]=v,lst[v]=u;
                    return 1;
                }
            }
        return 0;
    }

    void dfs(int fa,int u,int num) {
        if(num==1)dis[u]=dis[fa]+1;
        T.change(n-dis[u]+1,num);
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[v]&&v!=fa)dfs(u,v,num);
    }

    void query(int fa,int u) {
        dis[u]=dis[fa]+1,ans+=T.query(n-(max(1,limit-dis[u]))+1);
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[v]&&v!=fa)query(u,v);
    }

    void work() {
        lst[node1]=node2;
        memset(vis,0,sizeof(vis));
        int st=son[id<<1],ed=son[id<<1|1];
        if(nxt[st]==ed)swap(st,ed);
        int pos=st;vis[ed]=1;
        while(pos!=ed)vis[pos]=1,pos=nxt[pos];
        dis[ed]=0,pos=st;mx=0;
        while(pos!=ed)dfs(lst[pos],pos,1),pos=nxt[pos];dfs(lst[ed],ed,1);
        pos=ed;dis[st]=0;
        while(pos!=st)dfs(nxt[pos],pos,-1),query(nxt[pos],pos),pos=lst[pos];
    }
}R;

void find_rt(int fa,int u) {
    int res=0;siz[u]=1;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&(p>>1)!=id&&v!=fa)
            find_rt(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
    res=max(res,N-siz[u]);
    if(res<mx)mx=res,rt=u;
}

void dfs(int fa,int u,int dep) {
    mx=max(mx,dep),siz[u]=1;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&(p>>1)!=id&&v!=fa)
            dfs(u,v,dep+1),siz[u]+=siz[v];
}

bool cmp(int a,int b) {
    return depest[a]<depest[b];
}

void query(int fa,int u,int dep) {
    ans+=f[max(1,limit-dep)];
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&v!=fa&&(p>>1)!=id)query(u,v,dep+1);
}

void solve(int fa,int u,int dep) {
    g[dep]++;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&v!=fa&&(p>>1)!=id)solve(u,v,dep+1);
}

void work(int u,int size) {
    N=size,mx=rt=n+1,find_rt(0,u),u=rt,vis[u]=1,tot=0;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&(p>>1)!=id) {
            V[++tot]=v,mx=0;
            dfs(u,v,2),depest[v]=mx;
        }
    sort(V+1,V+tot+1,cmp);
    for(int i=1;i<=tot;i++) {
        query(u,V[i],1);
        solve(u,V[i],2);
        for(int j=depest[V[i]];j;j--)
            g[j]+=g[j+1],f[j]+=g[j];
        for(int j=1;j<=depest[V[i]];j++)g[j]=0;
    }
    ans+=f[limit];
    for(int i=1;i<=depest[V[tot]];i++)f[i]=g[i]=0;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&(p>>1)!=id)work(v,siz[v]);
}

int main() {
    n=read(),m=read(),limit=read();
    for(int i=1;i<=m;i++) {
        int a=read(),b=read();
        add(a,b),add(b,a);
    }
    if(m==n)R.find_ring(0,1);
    memset(vis,0,sizeof(vis));
    work(1,n);
    if(m==n)R.work();
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/AKMer/p/10123878.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值