码迷,mamicode.com
首页 > 其他好文 > 详细

2018.12.28-dtoj-3648-寝室管理

时间:2018-12-29 01:05:59      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:而且   close   for   def   name   string   技术分享   双向   ati   

题目描述:

T64有一个好朋友,叫T128。T128是寄宿生,并且最近被老师叫过去当宿管了。宿管可不是一件很好做的工作,碰
巧T128有一个工作上的问题想请T64帮忙解决。T128的寝室条件不是很好,所以没有很多钱来装修。礼间寝室仅由n
-1条双向道路连接,而且任意两间寝室之间都可以互达。最近,T128被要求对一条路径上的所有寝室进行管理,这
条路径不会重复经过某个点或某条边。但他不记得是哪条路径了。他只记得这条路径上有不少于k个寝室。于是,
他想请T64帮忙数一下,有多少条这样的路径满足条件。嗯…还有一个问题。由于最近有一些熊孩子不准晚上讲话
很不爽,他们决定修筑一条“情报通道”,如果通道建成,寝室就变成了一个N个点N条边的无向图。并且,经过“
情报通道”的路径也是合法的。T128心想:通道建成之前,T64还有一个高效的算法帮我数路径条数,但是通道建
成之后,他还有办法吗?对,T64手忙脚乱,根本数不清有多少条路径。于是他找到了你

算法标签:点分治

思路:

当m==n-1时是裸的点分治,考虑再加一条边,统计对答案的贡献,对于加边后构成的环,依此枚举断掉每一条边的贡献,怎么断不会算重的问题看代码吧。

以下代码:

技术分享图片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e5+5;
int k,m,w,r,c,n,t[M],l[M],h[N],f[N],p[N],s[N],d[N];
bool b[N];ll a[N],q;
int get(int x){return !f[x]?x:f[x]=get(f[x]);}
void insert(int x,int y){
    t[++c]=y,l[c]=h[x],h[x]=c;
    t[++c]=x,l[c]=h[y],h[y]=c;
}
void add(int x,int y=1){
    for(;x<=n;x+=x&-x)
        if(p[x]!=c) p[x]=c,a[x]=y;
        else a[x]+=y;
    }
ll sum(int x){
    if(x<0) return 0;
    ll y=0;
    for(;x;x-=x&-x)
        if(p[x]==c) y+=a[x];
    return y;
}
#define z t[i]
void root(int x,int y){
    int c=0;s[x]=1;
    for(int i=h[x];i;i=l[i]){
        if(b[z] || y==z) continue;
        root(z,x);
        s[x]+=s[z];
        c=max(c,s[z]);
    }
    c=max(c,m-s[x]);
    if(c<w) w=c,r=x;
}
void dfs1(int x,int y,int w){
    s[x]=1;q+=sum(n)-sum(k-w-1);
    for(int i=h[x];i;i=l[i]){
        if(b[z] || y==z) continue;
        dfs1(z,x,w+1);s[x]+=s[z];
    }
}
void dfs2(int x,int y,int w){
    add(w+1);
    for(int i=h[x];i;i=l[i])
        if(!b[z] && y!=z) dfs2(z,x,w+1);
}
void solve(int x){
    ++c;b[x]=1;add(1);
    for(int i=h[x];i;i=l[i])
        if(!b[z]) dfs1(z,x,1),dfs2(z,x,1);
    for(int i=h[x];i;i=l[i]){
        if(b[z]) continue;
        w=1e9;m=s[z];
        root(z,x);solve(r);
    }
}
void dfs3(int x){
    add(d[x]);
    for(int i=h[x];i;i=l[i]){
        if(z==f[x]) continue;
        f[z]=x;d[z]=d[x]+1;
        dfs3(z);
    }
}
void dfs4(int x,int y){
    add(d[x],-1);
    for(int i=h[x];i;i=l[i])
        if(z!=y && !b[z]) dfs4(z,x);
}
void dfs5(int x,int y,int w){
    q+=sum(n)-sum(k-w-1);
    for(int i=h[x];i;i=l[i])
        if(z!=y && !b[z]) dfs5(z,x,w+1);
}
int main(){
    int x,y,u,v,g=0,h;
    scanf("%d%d%d",&n,&m,&k);
    while(m--){
        scanf("%d%d",&x,&y);
        u=get(x),v=get(y);
        if(u==v) g=x,h=y;
        else f[u]=v,insert(x,y);
    }
    c=0;m=n,w=1e9;
    root(1,0);
    solve(r);
    if(!g) return !printf("%lld\n",q);
    memset(b,0,n+1);
    ++c;f[g]=0;d[g]=1;dfs3(g);
    for(int i=h;i;i=f[i]) b[i]=1;
    for(int i=h,j=1;i!=g;i=f[i],++j) dfs4(i,0),dfs5(i,0,j);
    printf("%lld\n",q);
    return 0;
}
View Code

 

 

2018.12.28-dtoj-3648-寝室管理

标签:而且   close   for   def   name   string   技术分享   双向   ati   

原文地址:https://www.cnblogs.com/Jessie-/p/10193461.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!