zoukankan      html  css  js  c++  java
  • 【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治

    3648: 寝室管理

    Time Limit: 40 Sec  Memory Limit: 512 MB
    Submit: 239  Solved: 106
    [Submit][Status][Discuss]

    Description

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

    Input

       第一行为三个正整数N,M,K(2 ≤ K ≤ N),代表有n间寝室,m条边连接它们n-1 ≤ m ≤ N;m= n-1意味着“情报遁道”未被修好;m=n意味着“情报通道”已被修好),以及题目描述中的K。
      接下来m行,每行两个正整数z,y,代表第x间寝室与第y间寝室之间有一条双向边。

    Output

    仅包含一个整数,代表经过至少K间寝室的路径条数。

    Sample Input

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

    Sample Output

    20

    HINT

    N≤100000      
    K≤N
    M=N           

    Source

    Solution

    首先考虑M=N-1的情况,路径问题,树上可以直接点分治算出

    M=N的情况,就是环套树,方法是一样的,先拆环,用处理树的方法计算答案,然后单独计算经过环的答案

    找环的过程,并查集即可....

    统计答案,利用树状数组即可,或者Splay,线段树都可以,具体作用就是处理完一颗子树,将答案加入树状数组,再处理其他子树的时候,再加入答案进行计算,保证不出现遗漏

    考虑经过环的情况,从路径上找到环,更新环上点的所有权,再进行一次计算,即可

    或者,正反扫两次环亦可

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    void Freopen() {freopen("sport.in","r",stdin); freopen("sport.out","w",stdout);}
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define MAXN 100010
    int N,M,K;
    struct EdgeNode{int next,to;}edge[MAXN<<1];
    int head[MAXN],cnt=1;
    void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
    void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
    int CA,CB,f[MAXN],visit[MAXN],mark[MAXN],size[MAXN],deep[MAXN],Size,root,from[MAXN],C[MAXN],cn;
    long long Ans;
    struct BinaryIndexedTree
    {
        int NUM,bl[MAXN],id;
        long long tree[MAXN];
        int lowbit(int x) {return x&(-x);} 
        void Add(int pos,int D) 
            {
                for (int i=pos; i<=NUM; i+=lowbit(i)) 
                    if (bl[i]!=id)
                        tree[i]=1,bl[i]=id;
                    else
                        tree[i]+=D;
            }
        void Sub(int pos,int D)
            {
                for (int i=pos; i<=NUM; i+=lowbit(i)) 
                    tree[i]+=D;
            } 
        long long Query(int pos)
            {
                if (pos<0) return 0;
                long long re=0;
                for (int i=pos; i; i-=lowbit(i))
                    if (bl[i]==id) re+=tree[i];
                return re;
            }
        long long Query(int L,int R) {return Query(R)-Query(L-1);}
    }bit;
    struct UnionFind
    {
        int Fa[MAXN];
        void init() {for (int i=1; i<=N; i++) Fa[i]=i;}
        int find(int x) {if (Fa[x]==x) return x; else return Fa[x]=find(Fa[x]);}
        void merge(int x,int y) {int f1=find(x),f2=find(y); if (f1!=f2) Fa[f1]=f2;}
    }uf;
    void DFSroot(int now,int last)
    {
        f[now]=0,size[now]=1;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=last && !visit[edge[i].to])
                {
                    DFSroot(edge[i].to,now);
                    size[now]+=size[edge[i].to];
                    f[now]=max(f[now],size[edge[i].to]);
                }
        f[now]=max(f[now],Size-size[now]);
        if (f[now]<f[root]) root=now;
    }
    void CalcT(int now,int last,int dep)
    {
        Ans+=bit.Query(K-dep,N);
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=last && !visit[edge[i].to])
                CalcT(edge[i].to,now,dep+1);
    }
    void AddAns(int now,int last,int dep)
    {
        bit.Add(dep+1,1);
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=last && !visit[edge[i].to])
                AddAns(edge[i].to,now,dep+1);
    }
    void Divide(int now)
    {
        visit[now]=1;
        bit.id++; bit.Add(1,1);
        for (int i=head[now]; i; i=edge[i].next)
            if (!visit[edge[i].to])
                CalcT(edge[i].to,now,1),AddAns(edge[i].to,now,1);
        for (int i=head[now]; i; i=edge[i].next)
            if (!visit[edge[i].to])
                {
                    f[root=0]=Size=size[edge[i].to];
                    DFSroot(edge[i].to,now);
                    Divide(root);
                }
    }
    void SolveT()
    {
        f[root=0]=N; Size=N;
        DFSroot(1,0);
        Divide(root);
    }
    void GetCircle(int now,int last,int dep)
    {
        from[now]=last; bit.Add(dep,1); deep[now]=dep;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=last)
                GetCircle(edge[i].to,now,dep+1);
    }
    void CalcCT(int now,int last,int D)
    {
        Ans+=bit.Query(K-D,N);
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=last && !mark[edge[i].to])
                CalcCT(edge[i].to,now,D+1);
    }
    void SubAns(int now,int last)
    {
        bit.Sub(deep[now],-1);
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=last && !mark[edge[i].to])
                SubAns(edge[i].to,now);
    }
    void SolveCT()
    {
        bit.id++; GetCircle(CA,0,1);
        for (int i=CB; i; i=from[i]) C[++cn]=i,mark[i]=1;
        for (int i=1; i<cn; i++) SubAns(C[i],0),CalcCT(C[i],0,i);
    }
    int main()
    {
    //  Freopen();
        N=read(); M=N; M=read(); K=read();
        uf.init(); bit.NUM=N;
        for (int x,y,i=1; i<=M; i++)
            { 
                x=read(),y=read();
                if (uf.find(x)!=uf.find(y)) InsertEdge(x,y),uf.merge(x,y); else CA=x,CB=y;
            }
        SolveT();
        if (N==M) SolveCT();
        printf("%lld
    ",Ans);
        return 0;
    } 

    这里有个坑,某种大环的情况,会被卡RE....

  • 相关阅读:
    第一次随笔
    SDN第二次上机作业
    SDN第二次作业
    个人作业——软件产品案例分析
    SDN第一次上机作业
    SDN第一次作业
    个人技术博客(1/2)android布局技巧
    个人技术博客(α)
    团队作业——随堂小测(同学录)
    Alpha冲刺报告(2/12)(麻瓜制造者)
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5750914.html
Copyright © 2011-2022 走看看