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
  • 相关阅读:
    (转)A*算法详解及习题
    BZOJ1444[Jsoi2009]有趣的游戏——AC自动机+概率DP+矩阵乘法
    交通——set+dsu on tree
    [Codeforces1132G]Greedy Subsequences——线段树+单调栈
    BZOJ4482[Jsoi2015]套娃——贪心+set
    BZOJ4477[Jsoi2015]字符串树——可持久化trie树
    BZOJ4475[Jsoi2015]子集选取——递推(结论题)
    [UOJ86]mx的组合数——NTT+数位DP+原根与指标+卢卡斯定理
    BZOJ2428[HAOI2006]均分数据——模拟退火
    BZOJ4712洪水——动态DP+树链剖分+线段树
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10385783.html
Copyright © 2011-2022 走看看