zoukankan      html  css  js  c++  java
  • D4 树的直径、重心以及基环树

    第一题第二题鉴上我前几篇博客poj1985 poj1849https://www.cnblogs.com/Tyouchie/p/10384379.html

    第三题:数的重心;poj1655

    来自sjh大佬的版子,邻接表写法

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    using namespace std;
    const int maxn=2e4+10;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch) && ch^'-') f=-1, ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int vis[maxn],siz[maxn],ans,pos,n;
    int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
    inline void add(int x,int y)
    {
        ver[++len]=y,Next[len]=head[x],head[x]=len;
    }
    inline void dfs(int x)
    {
        vis[x]=1,siz[x]=1;
        int maxpart=0;
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (!vis[y])
            {
                dfs(y);
                siz[x]+=siz[y];
                maxpart=max(maxpart,siz[y]);
            }
        }
        maxpart=max(maxpart,n-siz[x]);
        if (maxpart<ans || (maxpart==ans && x<pos))
            ans=maxpart,pos=x;
    }
    int main()
    {
        int t;read(t);
        while (t--)
        {
            read(n);
            memset(vis,0,sizeof(vis));
            memset(head,0,sizeof(head));
            len=pos=0;
            ans=0x3f3f3f3f;
            for (int x,y,i=1;i<n;++i)
            {
                read(x);read(y);
                add(x,y);add(y,x);
            }
            dfs(1);
            printf("%d %d
    ",pos,ans);
        }
        return 0;
    }
    View Code

    我写的邻接矩阵

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    using namespace std;
    const int maxn=2e4+10;
    const int INF=0x3f3f3f3f;
    vector<int>G[maxn];
    int son[maxn];
    int ans,n,balance,t;
    
    void dfs(int v,int fa)
    {
        son[v]=1;
        int d=G[v].size();
        int pre_balance=0;
        for (int i=0;i<d;i++)
        {
            int k=G[v][i];
            if (k!=fa)
            {
                dfs(k,v);
                son[v]+=son[k];
                pre_balance=max(pre_balance,son[k]);
            }
        }
        pre_balance=max(pre_balance,n-son[v]);
        if (pre_balance<balance || (pre_balance==balance&&v<ans))
        {
            ans=v;
            balance=pre_balance;
        }
    }
    int main()
    {
        cin>>t;
        while (t--)
        {
            scanf("%d",&n);
            for (int i=1;i<=n;++i)
                G[i].clear();
            for (int i=1;i<n;i++)
            {
                int s,e;
                scanf("%d%d",&s,&e);
                G[s].push_back(e);
                G[e].push_back(s);
            }
            memset(son,0,sizeof(son));
            ans=0;balance=INF;
            dfs(1,0);
            cout<<ans<<' '<<balance<<endl;
        }
        return 0;
    }
    View Code

    Noip 2018 旅行;luogu5022

    题意:一个n个点,m条边的连通图。可以从任意一个点出发,前往任意 一个相邻的未访问的结点,或沿着上一次来这个点的边返回。需要遍历 每一个点。每经过一个新的结点,就将这个结点写下来。最终可以得到 一个序列。求字典序最小的序列。 n ≤ 5000, m ≤ n。

    貌似用栈慢一点,洛谷最后一个点过不了,要用02优化;

    但是用栈维护单调性比较方便;

    n < m:对于树的情况,显然从1出发,每次从字典序最小的相邻结 点DFS即可。

    n = m: 对于有环的情况,由于环只有一个,我们可以将环找出来, 枚举删掉环上的每一条边,然后按树的情况求解即可。 时间复杂度O(n 2 )。

    #include <algorithm>
    #include <cctype>
    #include <cmath>
    #include <complex>
    #include <cstdio>
    #include <cstring>
    #include <deque>
    #include <functional>
    #include <list>
    #include <map>
    #include <iomanip>    
    #include <iostream>
    #include <queue>
    #include <set>
    #include <stack>
    #include <string>
    #include <vector>
    #define R register
    using namespace std;
    typedef long long ull;
    const int maxn = 5e3 + 100;
    
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
        while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
        return s * w;
    }
    
    inline void write(int x)
    {
        if(x < 0) putchar('-'),x = -x;
        if(x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
    
    vector <int> vec[maxn];
    int ans[maxn], edge[maxn][2], t[maxn], vis[maxn];
    int n, m, da, db, tsize = 0;
    
    inline void dfs(int x) {
        t[++tsize] = x; vis[x] = 1;
        int l = vec[x].size();
        for (R int i = 0; i < l; ++i) {
            int y = vec[x][i];
            if (!vis[y] && !((x == da && y == db) || (x == db && y == da))) dfs(y);
        }
        return ;
    }
    
    inline void check() {
        if (tsize != n) return ;
        for (R int i = 1; i <= n; ++i) {
            if (t[i] != ans[i]) {
                if (t[i] > ans[i]) return ;
                break;
            }
        }
        for (R int i = 1; i <= n; ++i) {
            ans[i] = t[i];
        }
        return ;
    }
    
    int main() {
        memset(ans, 0x3f, sizeof(ans));
        n = read(), m = read();
        for (R int i = 1; i <= m; ++i) {
            int a = read(), b = read();
            vec[a].push_back(b);
            vec[b].push_back(a);
            edge[i][0] = a;
            edge[i][1] = b;
        }
        for (R int i  = 1; i <= n; ++i) sort(vec[i].begin(), vec[i].end());
        if (n > m) {
            da = -1, db = -1;
            dfs(1);
            check();
        }
        else {
            for (R int i = 1; i <= m; ++i) {
                tsize = 0;
                da = edge[i][0];
                db = edge[i][1];
                memset(vis, 0, sizeof(vis));
                dfs(1);
                check();
            }
        }
        for (R int i = 1; i <= n; ++i) write(ans[i]), putchar(' ');
        return 0;
    }
    View Code

    第四题:BZOJ 1791

    跑一个基环树直径;

    来自石神的模板;

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+10;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int n,m,t;//t是标识符
    int ver[maxn<<1],edge[maxn<<1],next[maxn<<1],head[maxn],tot,du[maxn];
    void add(int x,int y,int z)
    {
        ver[++tot]=y,edge[tot]=z,next[tot]=head[x],head[x]=tot,++du[y];
    }
    int c[maxn];//环上的点
    int v[maxn];
    int q[maxn<<1];
    void bfs(int s,int t)
    {
        int l,r;
        q[l=r=1]=s;//手写队列维护
        c[s]=t;//标记连通块(看每个节点属于哪个基环树)
        while (l<=r)
        {
            for (int i=head[q[l]]; i; i=next[i])
                if (!c[ver[i]])
                {
                    q[++r]=ver[i];
                    c[ver[i]]=t;
                }
            l++;
        }
    }
    ll f[maxn];//每颗子树的直径
    ll d[maxn];//每个节点的子树大小
    void topsort()//找环操作顺便处理一种情况(直径不经过环)
    {
        int l=1,r=0,x,y;
        for (int i=1; i<=n; ++i)
            if (du[i]==1)//无向图度数为1
                q[++r]=i;
        while (l<=r)
        {
            for (int i=head[x=q[l]]; i; i=next[i])
                if (du[y=ver[i]]>1)//度大于1可更新答案
                {
                    d[c[x]]=max(d[c[x]],f[x]+f[y]+edge[i]);//子树内最长链
                    f[y]=max(f[y],f[x]+edge[i]);//f[x]表示x子树中离x最远的点的距离
                    if ((--du[y])==1)
                        q[++r]=y;
                }
            l++;
        }
    }
    ll a[maxn<<1];
    ll b[maxn<<1];
    void dp(int t,int x)
    {
        int m=0,i,l=0,r,y=x;
        do
        {
            a[++m]=f[y];
            du[y]=1;
            for (i=head[y]; i; i=next[i])
                if (du[ver[i]]>1)//点在环上
                {
                    y=ver[i];
                    b[m+1]=b[m]+edge[i];//b[i]表示环上x到i的距离
                    break;
                }
        } while (i);//此时答案为 f[i]+f[j]+dis[i][j]的最大值,dis[i][j]表示环上i到j的最远距离
        if (m==2)//跑到环外,需要特判
        {
            for (i=head[y]; i; i=next[i])
                if (ver[i]==x)
                    l=max(l,edge[i]);
            d[t]=max(d[t],f[x]+f[y]+l);
            return;
        }
        for (i=head[y]; i; i=next[i])//连接环的首尾
            if (ver[i]==x)
            {
                b[m+1]=b[m]+edge[i];
                break;
            }
        for (i=1; i<m; ++i)//由于是环,所以复制一份
        {
            a[m+i]=a[i];
            b[m+i]=b[m+1]+b[i];
        }
        q[l=r=1]=1;
        for (i=2; i<2*m; ++i)
        {
            while (l<=r && i-q[l]>=m) ++l;
            d[t]=max(d[t],a[i]+a[q[l]]+b[i]-b[q[l]]);
            while (l<=r && a[q[r]]+b[i]-b[q[r]]<=a[i]) --r;//单调队列维护
            q[++r]=i;
        }
    }
    int main()
    {
        read(n);
        for (int i=1; i<=n; ++i)
        {
            int x,y;
            read(x);read(y);
            add(i,x,y),add(x,i,y);
        }
        for (int i=1; i<=n; ++i)
            if (!c[i])
                bfs(i,++t);//统计有多少基环树
        topsort();//拓扑找环
    
        memset(v,0,sizeof(v));//重新利用v数组,当作基环树是否算过
        ll ans=0ll;
        for (int i=1; i<=n; ++i)
            if (du[i]>1 && !v[c[i]])//每个基环树只跑一遍并且此时i是环上一点
            {
                v[c[i]]=1;
                dp(c[i],i);//求基环树的直径
                ans+=d[c[i]];
            }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    第五题:LUOGU CF 835F

    第六题“:BZOJ 1040

    题意:n个点n条边的图,每个点都有点权,要求找到一个点集,点集中的 点相互之间不能有边相连,最大化点集的权值和。 1 ≤ n ≤ 106

    如果联通的话,就是一个基环树了,否则为基环树森林。

    这道题可 以简单的抽象为:基环树的最大独立集。

    如果是一棵树该怎么做?

    DP。

    f [i][0] =∑max(f [son[i]][0], f [son[i][1])

    f [i][1] =∑f [son[i]][0]

    在每一棵基环树的环上枚举一条边,记它的两个端点为u和v,然后 删掉这条边做树形dp即可。

    从该边的两个端点出发选择:

    1 强制不选u,v任意,环的贡献为以u做DP的f [u][0]。

    2 强制不选v,u任意,环的贡献为以v做DP的f [v][0]。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+5e2;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
    inline void add(int x,int y)
    {
        ver[++len]=y,Next[len]=head[x],head[x]=len;
    }
    int n,power[maxn],hate[maxn],vis[maxn],U,V;
    ll f[maxn],g[maxn];
    inline void dfs(int x,int fa)//dfs找环
    {
        vis[x]=1;
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (y!=fa)
            {
                if (!vis[y]) dfs(y,x);
                else
                {
                    vis[y]=1;
                    U=x,V=y;
                    return ;
                }
            }
        }
    }
    inline void tree_dp(int x,int fa,int rt,int ban)//ban 不选的点
    {
        vis[x]=1;
        f[x]=power[x];
        g[x]=0;
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (x==rt && i==ban) continue;
            if (y!=fa && y!=rt)
            {
                tree_dp(y,x,rt,ban);
                f[x]+=g[y];
                g[x]+=max(g[y],f[y]);
            }
        }
    }
    int main()
    {
        read(n);
        for (int i=1,k;i<=n;++i)
        {
            read(power[i]);read(hate[i]);
            add(i,hate[i]),add(hate[i],i);
        }
        ll ans=0;
        for (int i=1;i<=n;++i)
            if (!vis[i])
            {
                dfs(i,-1);
                int banu,banv;
                for (int i=head[U];i;i=Next[i])
                    if (ver[i]==V)
                    {
                        banu=i;
                        break;
                    }
                for (int i=head[V];i;i=Next[i])
                    if (ver[i]==U)
                    {
                        banv=i;
                        break;
                    }
                tree_dp(U,-1,U,banu);//断环为链并将断开的两个点强制其中一个点为根且不选,做一次树形DP
                ll uans=g[U];
                tree_dp(V,-1,V,banv);//对另一个点做同样操作
                ll vans=g[V];
                ans+=max(uans,vans);//取两次结果最大值加入ans
            }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    OLAP ODS项目的总结 平台选型,架构确定
    ORACLE ORA12520
    ORACLE管道函数
    ORACLE RAC JDBC 配置
    ORACLE RAC OCFS连接产生的错误
    ORACLE 启动和关闭详解
    OLAP ODS项目的总结 起步阶段
    ORACLE RAC 配置更改IP
    ORACLE RAC OCR cann't Access
    ORACLE RAC Debug 之路 CRS0184错误与CRS初始化
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10385783.html
Copyright © 2011-2022 走看看