Codeforces图论刷题

  • 题目链接:https://codeforces.com/problemset/problem/1439/B
  • 解题思路:很容易想到和拓扑排序类似的思路,删点判断答案。关键在于如何判断度数为k-1的完全图,这个地方只需要再每次删点之前判断一下就好了

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 1e5 + 5;
int T;
int n , m , k, flag = 1;
vector <int> edge[N];
queue <int> q;
int vis[N], vis2[N], deg[N];
vector <int> ve;
void solve ()
{
    while (!q.empty())
        q.pop();
    for (int i = 1 ; i <= n ; i ++)
        if (deg[i] < k)
            q.push (i), vis[i] = 1;

    while (!q.empty())
    {
        int u = q.front ();
        q.pop ();
        vis[u] = 1;
        if (deg[u] == k-1)
        {
            ve.clear();
            ve.pb (u);
            for (auto it : edge[u])
            {
                if (!vis2[it])
                    ve.pb(it);
            }
            int mark = 1;
            for (int i = 0 ; i < ve.size() ; i ++)
            {
                for (int j = 0 ; j < ve.size() ; j++)
                {
                    if (ve[i] == ve[j]) break;
                    if(!binary_search(edge[ve[i]].begin(),edge[ve[i]].end(),ve[j]))
                        mark = 0;
                }
            }
            if (mark)
            {
                flag = 0;
                break;
            }
            ve.clear();
        }
        vis2[u] = 1;
        for (auto it : edge[u])
        {
            deg[it] --;
            if (deg[it] < k && !vis[it]) {
                q.push(it), vis[it] = 1;
            }
        }
    }
}
int main()
{
    CLOSE;
    cin >> T;
    while (T --)
    {
        flag = 1;
        cin >> n >> m >> k;
        for (int i = 1 ; i <= m ; i ++)
        {
            int u , v;
            cin >> u >> v;
            edge[u].pb (v);
            edge[v].pb (u);
        }
        for (int i = 1 ; i <= n ; i ++)
            sort (edge[i].begin(), edge[i].end()), deg[i] = sz(edge[i]);
        solve ();


        if (!flag)
        {
            cout << 2 << endl;
            for (auto it : ve)
                cout << it << ' ';
            cout << endl;
        }
        else
        {
            set <int> s;
            for (int i = 1 ; i <= n ; i ++)
            {
                for (auto it : edge[i])
                    if (!vis[it])
                        s.insert (it);
            }
            if (sz(s) == 0)
                cout << -1 << endl;
            else
            {
                cout << 1 << ' ' << sz (s) << endl;
                for (auto it : s)
                    cout << it << ' ';
                cout << endl;
            }
        }
        for (int i = 1 ; i <= n ; i ++)
            edge[i].clear(), deg[i] = vis[i] = vis2[i] = 0;
    }
}
  • 题目链接: https://codeforces.com/contest/1408/problem/E
  • 解题思路: 本题的难点在于建立出二分图模型,将集合作为X部,节点作为Y部,然后根据集合重的节点连边。如果二分图中出现环,那么原图中也一定会不合法,所以问题转换为如何求出二分图中的最大生成树

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}


const int N = 2e5 + 5;
int n , m;
int a[N], b[N], fa[N << 1];
int cnt = 0;
ll res = 0;
struct node {
    int u , v , w;
    bool operator < (const node& no) const {
        return w > no.w;
    }
}edge[N << 1];
int findroot (int x)
{
    if (fa[x] == x) return x;
    return fa[x] = findroot (fa[x]);
}
bool merge (int x, int y)
{
    int fax = findroot (x);
    int fay = findroot (y);
    if (fax != fay)
    {
        fa[fax] = fay;
        return true;
    }
    return false;
}
int main()
{
    CLOSE;
    cin >> m >> n;
    int tot = n;
    for (int i = 1 ; i <= m ; i ++)
        cin >> a[i];
    for (int i = 1 ; i <= n ; i ++)
        cin >> b[i];
    for (int i = 1 ; i <= m ; i ++)
    {
        ++ tot;
        int s;
        cin >> s;
        for (int j = 1 ; j <= s ; j ++)
        {
            int v;
            cin >> v;
            edge[++cnt].u = tot, edge[cnt].v = v, edge[cnt].w = a[i] + b[v];
            res += edge[cnt].w;
        }
    }
    sort (edge+1,edge+1+cnt);
    for (int i = 1 ; i <= tot + 5; i ++) fa[i] = i;
    for (int i = 1 ; i <= cnt ; i ++)
    {
        int u = edge[i].u, v = edge[i].v, w = edge[i].w;
        if (merge (u, v))
            res -= w;
    }
    cout << res << endl;
}
  • 题目链接:https://codeforces.com/problemset/problem/1394/B
  • 解题思路:没有什么好的想法,看见 K K K的值比较小,就想到了全排列枚举,然后去 O ( N ) O(N) O(N)判断环,然后就没了。看题解之后发现,完全可以 H a s h Hash Hash一下,处理出每个度的第 i i i大的边可到达的集合,然后枚举全排列后检查一下是否能够到达 N N N个点就好
const int N = 2e5 + 5;
int n ,m ,k;
int deg[N], ans = 0;
int val[N], flag = 0, p[15][15];
vector <pii> edge[N];
int tmp[15];
void dfs (int now)
{
    if (now > k)
    {
        int mark = 0;
        for (int i = 1 ; i <= k ; i ++)
            mark += p[i][tmp[i]];
        if (mark == flag)
            ans ++;
        return ;
    }
    for (int i = 1 ; i <= now ; i ++)
        tmp[now] = i, dfs (now + 1);
}
int main(void)
{
    CLOSE;
    srand(time(0));
    cin >> n >> m >> k;
    for (int i = 1 ; i <= n ; i ++)
        val[i] = rand (), flag += val[i];
    for (int i = 1 ; i <= m ; i ++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        deg[u] ++;
        edge[u].pb(mk(w,v));
    }
    for (int i = 1 ; i <= n ; i ++)
        sort (edge[i].begin(), edge[i].end());
    for (int i = 1 ; i <= n ; i ++)
    {
        for (int j = 0 ; j < edge[i].size() ; j ++)
        {
            p[deg[i]][j+1] += val[edge[i][j].se];
        }
    }
    dfs (1);
    cout << ans << endl;
}
  • 题目链接:https://codeforces.com/contest/1391/problem/E
  • 解题思路:比较显然的一个题目,在图上搞一颗搜索树,然后看最深的节点是不是超过题目要求,如果不超过,那么按搜索树度数分层后,每层俩俩匹配即可,多余的一个点不要
const int N = 5e5 + 5;
int T;
int n , m;
vector <int> edge[N];
int dep[N], fa[N], maxn, vis[N];
vector <int> node[N];
void dfs (int u , int pa)
{
    for (auto v : edge[u])
    {
        if (v != pa && !dep[v])
        {
            fa[v] = u;
            dep[v] = dep[u] + 1;
            node[dep[v]].pb(v);
            maxn = max (maxn, dep[v]);
            dfs (v, u);
        }
    }
}
int main(void)
{
    CLOSE;
    cin >> T;
    while (T --)
    {
        maxn = 0;
        cin >> n >> m;
        for (int i = 1 ; i <= m ; i ++)
        {
            int u , v;
            cin >> u >> v;
            edge[u].pb (v);
            edge[v].pb (u);
        }
        vector <int> ve;
        int ok = 1;
        dep[1] = 1;
        dfs (1, 1);
        for (int i = 1 ; i <= n ; i ++)
        {
            if (dep[i] >= (n + 1)/2)
            {
                ok = 0;
                cout << "PATH" << endl;
                cout << dep[i] << endl;
                int x = i;
                ve.pb (x);
                while (fa[x])
                    ve.pb(fa[x]), x = fa[x];
                break;
            }
        }
        if (!ok)
        {
            for (auto v : ve)
                cout << v << ' ';
            cout << endl;
            for (int i = 0 ; i <= n ; i ++)
                edge[i].clear(), node[i].clear(), dep[i] = vis[i] = fa[i] = 0;
            continue;
        }

        for (int i = 0 ; i <= maxn ; i += 2)
        {
            for (auto v : node[i])
                vis[v] = 1;
        }

        cout << "PAIRING" << endl;
        vector <int> ans;
        int len = 0;
        for (int i = 1 ; i <= maxn ; i ++)
            len += (sz(node[i])/2);
        cout << len << endl;
        for (int i = 1 ; i <= maxn ; i ++)
            for (int j = 0 ; j < sz(node[i]) - 1 ; j += 2)
                cout << node[i][j] << ' ' << node[i][j+1] << endl;
        for (int i = 1 ; i <= n ; i ++)
            edge[i].clear(), node[i].clear(), dep[i] = vis[i] = 0;
    }
}
  • 题目链接:https://codeforces.com/problemset/problem/1364/D
  • 解题思路:搞出一颗搜索树之后,然后利用非树边找一下环就好了。如果存在 L e n ( c y c l e ) < = k Len(cycle) <= k Len(cycle)<=k 直接输出,如果不存在找一个 L e n ( c y c l e ) > k Len(cycle) > k Len(cycle)>k 的环输出一半,如果不存在环直接二分图染色就行
const int N = 5e5 + 5;
int T;
int n , m , k;
struct node {
    int f, t, next, mark;
}edge[N];
int head[N], cnt, fa[N];
void add (int f, int t)
{
    edge[cnt].mark = 0;
    edge[cnt].f = f;
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
//LCA部分
int bz[N][30] , deep[N];
void dfs(int rt, int dep)
{
    deep[rt] = dep;
    for (int i = head[rt] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].t;
        if(!deep[v])
        {
            fa[v] = rt;
            edge[i].mark = edge[i^1].mark = 1;
            bz[v][0] = rt;
            dfs(v,dep + 1);
        }
    }
}
void deal()
{
    for (int i = 1 ; i <= 25 ; i ++)
        for (int j = 1 ; j <= n ; j ++)
            bz[j][i] = bz[bz[j][i-1]][i-1];
}
int LCA(int x,int y)
{
    if(deep[x] < deep[y])
        swap(x,y);
    int t = deep[x] - deep[y];
    for(int i = 0; (1<<i) <= t ; i ++)
        if((1<<i)&t)x=bz[x][i];
    for(int i=19 ; i >= 0 ; i--)
        if(bz[x][i] ^ bz[y][i])
            x=bz[x][i], y=bz[y][i];
    if(x==y) return x;
    return bz[x][0];
}
vector <int> ve;
int ok = 0, ok2 = 0;
void solve ()
{
    for (int i = 0 ; i < cnt ; i += 2)
    {
        if (!edge[i].mark)
        {
            int u = edge[i].f , v = edge[i].t;
            int lca = LCA (u, v);
            int len = deep[u] + deep[v] - 2 * deep[lca] + 1;
            if (len <= k)
            {
                cout << 2 << endl;
                if (deep[u] > deep[v])
                    swap (u, v);
                if (u == lca)
                {
                    while (v != lca)
                    {
                        ve.pb (v);
                        v = fa[v];
                    }
                    ve.pb (lca);
                }
                else
                {
                    while (u != lca)
                    {
                        ve.pb(u);
                        u = fa[u];
                    }
                    while (v != lca)
                    {
                        ve.pb(v);
                        v = fa[v];
                    }
                    ve.pb(lca);
                }
                cout << sz(ve) << endl;
                for (auto it : ve)
                    cout << it << ' ';
                cout << endl;
                ok = 1;
                return ;
            }
        }
    }
}
int main()
{
    CLOSE;
    mem (head, -1);
    cin >> n >> m >> k;
    for (int i = 1 ; i <= m ;  i ++)
    {
        int u , v;
        cin >> u >> v;
        add (u, v);
        add (v, u);
    }
    dfs (1, 1);
    deal ();
    solve ();
    if (ok)
        return 0;
    for (int i = 0 ; i < cnt ; i += 2)
    {
        if (!edge[i].mark)
        {
            int u = edge[i].f , v = edge[i].t;
            int lca = LCA (u, v);
            int len = deep[u] + deep[v] - 2 * deep[lca] + 1;
            if (len > k)
            {
                ok2 = 1;
                cout << 1 << endl;
                if (deep[u] > deep[v])
                    swap (u, v);
                if (u == lca)
                {
                    while (v != lca)
                    {
                        ve.pb (v);
                        v = fa[v];
                    }
                    ve.pb (lca);
                }
                else
                {
                    while (u != lca)
                    {
                        ve.pb(u);
                        u = fa[u];
                    }
                    vector <int> ve2;
                    ve.pb(lca);
                    while (v != lca)
                    {
                        ve.pb(v);
                        v = fa[v];
                    }
                    reverse(ve2.begin(),ve2.end());
                    for (auto it : ve2)
                        ve.pb(it);
                }
                int cur = 0;
                for (int j = 1 ; j <= (k+1)/2 ; j ++)
                    cout << ve[cur] << ' ', cur += 2;
                return 0;
            }
        }
    }
    if (ok2)
        return 0;
    int tot = 0;
    for (int i = 1 ; i <= n ; i ++)
        if (deep[i]%2)
            tot ++;
    int p = 0;
    cout << 1 << endl;
    if (tot >= (k+1)/2)
    {
        for (int i = 1 ; i <= n ; i ++)
            if (deep[i]%2) {
                cout << i << ' ';
                p += 1;
                if (p == (k+1)/2)
                    break;
            }
    }
    else
    {
        for (int i = 1 ; i <= n ; i ++)
            if (deep[i]%2==0){
                cout << i << ' ';
                p += 1;
                if (p == (k+1)/2)
                    break;
            }
    }
    cout << endl;
}
  • 题目链接:https://codeforces.com/contest/1463/problem/E
  • 解题思路:看完题之后,会发现本题存在一个先后顺序,如果没有特殊限制,那么直接建图之后拓扑排序就好了,但是现在有特殊条件,那么我们只需要强制把特殊条件的点捆绑在一起,这个地方我们利用并查集就好了,注意,这里的并查集以 X X X为根,输出的时候 d f s dfs dfs一下就好了
const int N = 3e5 + 5;
int n , k, deg[N], Deg[N];
vector <int> edge[N],E[N], ans,Edge[N];
queue <int> q;
vector <pii> ip;
int fa[N], tot;
map <pii, int> ma;
int findroot (int x)
{
    if (fa[x] == x)
        return x;
    return fa[x] = findroot (fa[x]);
}
bool merge (int x, int y)
{
    int fax = findroot (x);
    int fay = findroot (y);
    if (fax != fay) {
        fa[fay] = fax;
        return true;
    }
    return false;
}
void topo()
{
    for (int i = 1 ; i <= n ; i ++) {
        if (!deg[i] && i == findroot (i))
            q.push(i), ans.pb(i);
    }
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (auto v : edge[u])
        {
            deg[v] --;
            if (!deg[v])
                q.push(v), ans.pb(v);
        }
    }
}
bool topo2 ()
{
    for (int i = 1 ; i <= n ; i ++)
        if (!Deg[i])
            q.push(i);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (auto v : Edge[u])
        {
            Deg[v] --;
            if (!Deg[v])
                q.push(v);
        }
    }
    for (int i = 1 ; i <= n ; i ++)
        if (Deg[i])
            return true;
    return false;
}
void dfs (int x)
{
    cout << x << ' ';
    for (auto v : E[x])
        dfs (v);
}
int main (void)
{
    CLOSE;
    int ok = 1;
    cin >> n >> k;
    for (int i = 1 ; i <= n ; i ++)
        fa[i] = i;
    for (int i = 1 ; i <= n ; i ++)
    {
        int x;
        cin >> x;
        if (x == 0)
            continue;
        ip.pb(mk(x,i));
        Edge[x].pb(i);
        Deg[i] ++;
    }
    while (k --)
    {
        int u , v;
        cin >> u >> v;
        Edge[u].pb(v);
        Deg[v] ++;
        E[u].pb(v);
        merge (u, v);
    }
    if (topo2())
    {
        cout << 0 << endl;
        return 0;
    }
    int sz = 0;
    for (int i = 1 ; i <= n ; i ++)
        if (findroot (i) == i)
            sz ++;
    for (auto it : ip)
    {
        int u = findroot (it.fi);
        int v = findroot (it.se);
        if (u != v)
        {
            edge[u].pb(v);
            deg[v] ++;
        }
    }
    topo();
    if (sz(ans) == sz)
        for (auto v : ans)
            dfs (v);
    else
        cout << 0;
    cout << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值