CF1103C Johnny Solving (Codeforces Round #534 (Div. 1)) 思维+构造

题目传送门

https://codeforces.com/contest/1103/problem/C

题解

这个题还算一个有难度的不错的题目吧。

题目给出了两种回答方式:

  1. 找出一条长度 \(\geq \frac nk\) 的路径;
  2. 找出 \(k\) 个简单环,满足长度不是 \(3\) 的倍数,并且每个环至少存在一个点不在别的环中。

很显然题目并不是要你随便挑一种回答方式开始单独研究。最有可能的情况是两种回答方式可以替补。



如果我们随便作出原图的一棵生成树,如果最长的路径长度 \(\geq \frac nk\),那么我们可以直接输出。否则,很容易发现,这棵树上至少有 \(k\) 个叶子。

很容易把 \(k\) 个叶子往第二种回答方式中的 \(k\) 个环上联想。但是第二个回答方式中的几个条件我们还都要满足。

对于“每个环至少存在一个点不在别的环中”这个条件,等价于就是说,每一个叶子不会和其余的叶子有连边。dfs 树可以保证这个性质,同时 dfs 树还可以保证,每一个点出来的非树边一定是返祖边。那么,建立 dfs 树以后,每个环就是每一个叶子和它的一些祖先之间的事情了。

接着考虑环的长度不是 \(3\) 的倍数的这个条件。

首先,我们不妨假设叶子 \(x\) 连向的两条返祖边指向的点分别为 \(x\) 的祖先为 \(f_1\) 和 \(f_2\)。

那么我们得到了三个环 \(x \to f_1 \to x\),\(x\to f_2 \to x\),\(f_1\to f_2 \to x\)。

然后三条路径的长度为别为 \(dep_x - dep_{f_1} + 1\),\(dep_x - dep_{f_2} + 1\),\(dep_{f_1} - dep_{f_2} + 2\)。

如果 \(dep_x - dep_{f_1} + 1\) 和 \(dep_x - dep_{f_2} + 1\) 都是 \(3\) 的倍数(否则输出其中的一个就可以了),那么就是说 \((dep_x - dep_{f_1}) - (dep_x - dep_{f_2}) + 2 = dep_{f_1} - dep_{f_2} + 2\) 模上 \(3\) 应该是 \(2\)。

也就是说,上面的三个环中至少存在一个环的长度不是 \(3\) 的倍数。输出哪一个即可。



时间复杂度 \(O(n+m)\)。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;
}

const int N = 2.5e5 + 7;
const int M = 5e5 + 7;

int n, m, k, nk;
int fz[N][3], f[N], chk[N], vis[N], dep[N];

struct Edge { int to, ne; } g[M << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }

inline void dfs(int x, int fa = 0) {
    f[x] = fa, vis[x] = 1, dep[x] = dep[fa] + 1;
    int &c = chk[x] = 1;
    for fec(i, x, y)
        if (!vis[y]) c = 0, dfs(y, x);
        else if (y != fa && fz[x][0] < 2) fz[x][++fz[x][0]] = y;
}

inline void work() {
    dfs(1);
    for (int i = 1; i <= n; ++i) if (dep[i] >= nk) {
        puts("PATH");
        printf("%d\n", dep[i]);
        for (int x = i; x; x = f[x]) printf("%d%c", x, " \n"[!f[x]]);
        return;
    }
    puts("CYCLES");
    int cnt = 0;
    for (int i = 1; i <= n; ++i) if (chk[i]) {
        ++cnt;
        if (dep[fz[i][1]] < dep[fz[i][2]]) std::swap(fz[i][1], fz[i][2]);
        if ((dep[i] - dep[fz[i][1]]) % 3 != 2) {
            int ans = 0;
            for (int x = i; x != f[fz[i][1]]; x = f[x]) ++ans;
            printf("%d\n", ans);
            for (int x = i; x != f[fz[i][1]]; x = f[x]) printf("%d%c", x, " \n"[x == fz[i][1]]);
        } else {
            if ((dep[i] - dep[fz[i][2]]) % 3 != 2) {
                int ans = 0;
                for (int x = i; x != f[fz[i][2]]; x = f[x]) ++ans;
                printf("%d\n", ans);
                for (int x = i; x != f[fz[i][2]]; x = f[x]) printf("%d%c", x, " \n"[x == fz[i][2]]);
            } else {
                int ans = 1;
                for (int x = fz[i][1]; x != f[fz[i][2]]; x = f[x]) ++ans;
                printf("%d\n", ans);
                for (int x = fz[i][1]; x != f[fz[i][2]]; x = f[x]) printf("%d ", x);
                printf("%d\n", i);
            }
        }
        if (cnt >= k) return;
    }
    assert(cnt >= k);
}

inline void init() {
    read(n), read(m), read(k), nk = (n - 1) / k + 1;
    int x, y;
    for (int i = 1; i <= m; ++i) read(x), read(y), adde(x, y);
}

int main() {
#ifdef hzhkk
    freopen("hkk.in", "r", stdin);
#endif
    init();
    work();
    fclose(stdin), fclose(stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/hankeke/p/cf1103c.html

时间: 2024-10-07 18:16:38

CF1103C Johnny Solving (Codeforces Round #534 (Div. 1)) 思维+构造的相关文章

Codeforces Round #534 (Div. 2)题解

Codeforces Round #534 (Div. 2)题解 A. Splitting into digits 题目大意 将一个数字分成几部分,几部分求和既是原数,问如何分可以使得分出来的各个数之间的差值尽可能小 解题思路 将n分成n个1相加即可 AC代码 #include<cstring> #include<string> #include<iostream> #include<cstdio> using namespace std; int main

Codeforces Round #534 (Div. 2)

A. Splitting into digits 题意:把一个数分成若干[1,9]之间的数字,使得这些数尽量相同. 思路:输出n个1. #include<bits/stdc++.h> #define CLR(a,b) memset(a,,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; int main(){ int n; cin>>n; int flag=0,j=2; prin

Codeforces Round #534 (Div. 2) D. Game with modulo(取余性质+二分)

D. Game with modulo 题目链接:https://codeforces.com/contest/1104/problem/D 题意: 这题是一个交互题,首先一开始会有一个数a,你最终的目的是要将它猜出来. 每次询问会输出"? x y",然后有: "x" (without quotes), if (x % a)≥(y % a). "y" (without quotes), if (x % a)<(y % a). 最多给你60次

Codeforces Round #534 (Div. 2) Solution

A. Splitting into digits Solved. 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int n; 5 6 void solve() 7 { 8 printf("%d\n", n); 9 for (int i = 1; i <= n; ++i) printf("%d%c", 1, " \n"[i == n]); 10 } 11 12 int

[ACM]Codeforces Round #534 (Div. 2)

一.前言 二.题面 A. Splitting into digits Vasya has his favourite number n. He wants to split it to some non-zero digits. It means, that he wants to choose some digits d1,d2,-,dk, such that 1≤di≤9 for all i and d1+d2+-+dk=n. Vasya likes beauty in everything

Codeforces Round #534 (Div. 1)

A 构造题 有一个44的方格 每次放入一个横向12或竖向2*1的方格 满了一行或一列就会消掉 求方案 不放最后一行 这样竖行就不会消 然后竖着的放前两行 横着的放第三行 循环放就可以啦 #include <cstdlib> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> const int N = 1e3 + 5; using namespace

Codeforces Round #306 (Div. 2) (构造)

A. Two Substrings 题意:给一个字符串,求是否含有不重叠的子串"AB"和"BA",长度1e5. 题解:看起来很简单,但是一直错,各种考虑不周全,最后只能很蠢的暴力,把所有的AB和BA的位置求出来,能有一对AB和BA不重叠即可. 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 char a[100005]; 5 vector<int> ab; 6 vector<i

题解——Codeforces Round #508 (Div. 2) T2 (构造)

按照题意构造集合即可 注意无解情况的判断 #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <vector> #include <map> using namespace std; int n,sum; int main(){ scanf("%d",&n); if(n==1){ printf

Codeforces Round #604 (Div. 2)D(构造)

构造,枚举起点,如果一个序列成立,那么将它reverse依然成立,所以两个方向(从小到大或从大到小)没有区别,选定一个方向进行探测,直到探测不到以后回头,只要所给数据能成立,那么能探测进去就能探测出来,否则就不能构造. 1 #define HAVE_STRUCT_TIMESPEC 2 #include<bits/stdc++.h> 3 using namespace std; 4 int num[7]; 5 int sum; 6 int main(){ 7 ios::sync_with_std