CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes)

CF系列题解(本场实况异常精彩,可惜题主要早八,结束就睡了,t宝有点可惜,jls我滴超人!!!)

CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes!)

  • 题目
    • A. Good Pairs
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • B. Subtract Operation
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • C. Make Equal With Mod
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • D. K-good
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • E. Equal Tree Sums
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码


题目

A. Good Pairs

原题链接

A. Good Pairs

题意

给定一个长度为 n n n 的数组 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an ,一个数对 ( i , j ) (i,j) (i,j) 被称为 时需要满足:对于所有的 1 ≤ k ≤ n 1≤k≤n 1kn ∣ a i − a k ∣ + ∣ a k − a j ∣ = ∣ a i − a j ∣ |a_i-a_k|+|a_k-a_j|=|a_i-a_j| aiak+akaj=aiaj 。找到一个 的数对,注意: i i i 可以等于 j j j

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 3000 ) t (1≤t≤3000) t(1t3000) ,表示有t组测试数据。
每个测试数据第一行包含一个个整数 n ( 1 ≤ n ≤ 1 0 5 ) n (1≤n≤10^5) n(1n105)
每个测试数据第二行包含 n n n 个整数 a 1 , a 2 , … , a n ( 1 ≤ a i ≤ 1 0 9 ) a_1,a_2,…,a_n (1≤a_i≤10^9) a1,a2,,an(1ai109) 表示给定的序列。
数组总长度不超过 2 ⋅ 1 0 5 2⋅10^5 2105

输出格式
输出任意一个 的数对。

输入样例:

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

输出样例:

2 3
1 2
1 1

题解

思路

由于所有的数均为正数,我们不妨假设 a i ≤ a k ≤ a j a_i≤a_k≤a_j aiakaj ,当 a i a_i ai 为序列最小值, a j a_j aj 为序列最大值时,把 ∣ a i − a k ∣ + ∣ a k − a j ∣ = ∣ a i − a j ∣ |a_i-a_k|+|a_k-a_j|=|a_i-a_j| aiak+akaj=aiaj 去掉绝对值得 a k − a i + a j − a k = a j − a i a_k-a_i+a_j-a_k=a_j-a_i akai+ajak=ajai ,左右两边恒成立。

由于可能存在多种情况,其他得构造方法也是可以的。

代码

#include 

// #define int long long

using namespace std;

typedef long long LL;

void solve()
{
    int n;
    cin >> n;

    vector<pair<int, int>> a(n);
    for (int i = 0; i < n; i ++ ) {
        int x;
        cin >> x;
        a[i] = {x, i};
    }

    sort(a.begin(), a.end());

    cout << a[0].second + 1 << " " << a[n - 1].second + 1 << "\n";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

B. Subtract Operation

原题链接

B. Subtract Operation

题意

给定序列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an ,每次操作可以选择一个数 a t a_t at ,其他得数减去 a t a_t at ,问执行 n − 1 n - 1 n1 次操作后能否得到给定数字 k k k

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 4 ) t (1≤t≤10^4) t(1t104) ,表示有t组测试数据。
每个测试数据第一行包含两个整数 n , k ( 2 ≤ n ≤ 2 ⋅ 1 0 5 , 1 ≤ k ≤ 1 0 9 ) n, k (2≤n≤2⋅10^5, 1≤k≤10^9) n,k(2n2105,1k109)
每个测试数据第二行包含 n n n 个整数 a 1 , a 2 , … , a n ( − 1 0 9 ≤ a i ≤ 1 0 9 ) a_1,a_2,…,a_n(−10^9≤a_i≤10^9) a1,a2,,an(109ai109) 表示给定的序列。
n n n 的总和不会超过 2 ⋅ 1 0 5 2⋅10^5 2105

输出格式
若能得到 k k k ,输出 YES ,否则输出 NO

输入样例:

4
4 5
4 2 2 7
5 4
1 9 1 3 4
2 17
17 0
2 17
18 18

输出样例:

YES
NO
YES
NO

题解

思路

直接讨论任意三个数 a i , a j , a k a_i,a_j,a_k ai,aj,ak 的情况。假设我们先删除 a i a_i ai ,序列变为 a j − a i , a k − a i a_j - a_i, a_k - a_i ajai,akai ,此时我们再删除 a j − a i a_j - a_i ajai ,序列变为 a k − a j a_k - a_j akaj ,我们发现最终剩余的数与你删除的过程无关,只与最后剩余的两个数的差有关,因此题目就变为了查找序列中是否存在一对 a i , a j a_i,a_j ai,aj ,使得它们的差为 k k k

直接枚举 O ( n 2 ) O(n^2) O(n2) 显然不行,用 set 随便搞一搞即可。

代码

#include 

#define int long long

using namespace std;

typedef long long LL;

void solve()
{
    int n, k;
    cin >> n >> k;

    set<int> S;
    for (int i = 0; i < n; i ++ ) {
        int x;
        cin >> x;
        S.insert(x);
    }

    for (auto x : S) {
        if (S.count(x + k)) {
            cout << "YES\n";
            return;
        }
    }
    cout << "NO\n";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

C. Make Equal With Mod

原题链接

C. Make Equal With Mod

题意

给定一个长度为 n n n 的数组,你可以选择一个数 k k k ,将数组中每个 a i a_i ai 变为 a i m o d k a_i mod k aimodk ,可以进行若干次操作,问最终的序列是否相等。

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 4 ) t (1≤t≤10^4) t(1t104) ,表示有t组测试数据。
每个测试数据第一行包含一个整数 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1≤n≤2⋅10^5) n(1n2105) ,表示序列长度。
每个测试数据第二行包含 n n n 个整数 a 1 , a 2 , … , a n ( − 1 0 9 ≤ a i ≤ 1 0 9 ) a_1,a_2,…,a_n(−10^9≤a_i≤10^9) a1,a2,,an(109ai109) 表示给定的序列。
n n n 的总和不会超过 2 ⋅ 1 0 5 2⋅10^5 2105

输出格式
若经过若干次操作序列相等,输出 YES ,否则输出 NO

输入样例:

4
4
2 5 6 8
3
1 1 1
5
4 1 7 0 8
4
5 9 17 5

输出样例:

YES
YES
NO
YES

题解

思路

分类讨论:

  1. 倘若原数组种没有 1 1 1 ,我们可以把所有数变为 0 0 0 ,即模上它自身。
  2. 倘若原数组中既有 0 0 0 ,又有 1 1 1 ,则无法满足若干次操作后数组各个元素相等。
  3. 接下来就是讨论变为 1 1 1 的情况,也非常简单,倘若有连续的两个整数 a i , a j a_i, a_j ai,aj ,无论如何操作,他们最终的模除结果都会差 1 1 1 ,因为每选取一个 k k k 是需要对所有的数进行模除操作的。

代码写的有点丑。。。

代码

#include 

#define int long long

using namespace std;

typedef long long LL;

void solve()
{
    int n;
    cin >> n;

    set<int> S;
    int cnt0 = 0, cnt1 = 0; // 统计0和1的数量
    for (int i = 0; i < n; i ++ ) {
        int x;
        cin >> x;
        S.insert(x);
        if (x == 1) cnt1 ++ ;
        if (x == 0) cnt0 ++ ;
    }

    if (cnt1 == 0) { // 第一种情况
        cout << "YES\n";
    } else if (cnt1 && cnt0) { // 第二种情况
        cout << "NO\n";
    } else { // 第三种情况
        for (auto x : S) {
            if (S.count(x + 1)) { // 判断是否有连续的两个数,这里采用了set判断
                cout << "NO\n";
                return; // 及时return
            }
        }
        cout << "YES\n";
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

D. K-good

原题链接

D. K-good

题意

给定一个整数 n n n ,问 n n n 能否分成 k ( k ≥ 2 ) k(k≥2) k(k2) 个数的和并且这 k k k 个数模 k k k 的余数两两不同。

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 5 ) t (1≤t≤10^5) t(1t105) ,表示有t组测试数据。
每组测试数据包含一个整数 n ( 2 ≤ n ≤ 1 0 18 ) n (2≤n≤10^{18}) n(2n1018)

输出格式
输出 k k k ,若无法分解,输出 − 1 -1 1

输入样例:

5
2
4
6
15
20

输出样例:

-1
-1
3
3
5

题解

思路

根据题意我们可得: n − k ( k − 1 ) / 2 ≡ 0 n - k(k - 1) / 2 \equiv 0 nk(k1)/20 ( m o d mod mod k k k) 。

  1. k k k 为奇数时, k k k n n n 的因子。
  2. k k k 为偶数时, k / 2 k/2 k/2 n n n 的因子。

不妨将 n n n 分解为 2 x × t 2^x ×t 2x×t t t t 为奇数。

奇数因子为 t t t ,即 k = t k=t k=t
偶数因子为 2 x 2^x 2x ,即 k / 2 = 2 x k/2=2^x k/2=2x

同时要满足 n − k ( k − 1 ) / 2 ≥ 0 n - k(k - 1) / 2 ≥0 nk(k1)/20 ,因此 k k k 我们可以取的尽可能小,而且由于 k ≥ 2 k≥2 k2 ,所以当 t = 1 t=1 t=1 k k k 只能取 2 x 2^x 2x 且此时的 2 x = n 2^x=n 2x=n ,不满足我们的不等式,因此无解。

剩下的我们由于需要满足上述不等式,因此取 m i n ( t , 2 x + 1 ) min(t, 2^{x+1}) min(t,2x+1) 即可(严格证明回头补一下,目前还没有太清楚,感觉可以就写了哈哈哈)。

代码

#include 

#define int long long

using namespace std;

typedef long long LL;

void solve()
{
    int n;
    cin >> n;

    int k = 1;
    while (n % 2 == 0) {
        n /= 2;
        k *= 2;
    }

    if (n == 1) {
        cout << "-1\n";
    } else {
        cout << min(n, k * 2) << "\n";
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

E. Equal Tree Sums

原题链接

E. Equal Tree Sums

题意

给出一棵树,请为每个结点赋点权,使得对于任意的 i i i ,删去点 i i i 后,树分为若干连通块,每个连通块的点权和相等。

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 4 ) t (1≤t≤10^4) t(1t104) ,表示有t组测试数据。
每个测试数据第一行包含一个个整数 n ( 3 ≤ n ≤ 1 0 5 ) n (3≤n≤10^5) n(3n105),表示节点个数。
接下来 n − 1 n-1 n1 行每行包含 2 2 2 个整数 u , v ( 1 ≤ u , v ≤ n ) u,v (1≤u,v≤n) u,v(1u,vn) 表示一条边。
n n n 的总和不超过 1 0 5 10^5 105

输出格式
每个测试用例输出 n n n 个整数 a 1 , a 2 , … , a n ( − 1 0 5 ≤ a i ≤ 1 0 5 , a i ≠ 0 ) a_1,a_2,…,a_n(−10^5≤a_i≤10^5,a_i≠0) a1,a2,,an(105ai105,ai=0) 表示节点的权值。

保证一定有解。

输入样例:

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

输出样例:

-3 5 1 2 2
1 1 1

题解

思路

直接上结论:
把所有点按层数染色为黑色和红色,若为红色,每个节点的权值为其的度数,若为红色,若为黑色,每个节点的权值为其度数的相反数(太神仙了这个构造)。

证明:
由于是按层进行染色,所以一个红色节点一定连接一个黑色节点,即红色节点的度数和 d r d_r dr 等于黑色节点的度数和 d b d_b db,而红色节点的权值和为其度数和 d r d_r dr,黑色节点的权值和为其度数和的相反数 − d b -d_b db,因此整棵树的权值为 0 0 0

接下来考虑任意一棵子树的情况:
CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes)_第1张图片
其实红色和黑色只用讨论一种即可(我们这边讨论左边的情况)。

倘若不存在蓝色边,那么左边黑色子树所有节点的权值为 0 0 0(已证),那么当加上蓝色边后,由于黑色子树连接的节点为红色,因此黑色子树的根节点应该为黑色,由于加上蓝色边只使得黑色子树的根节点度数加一,因此仅会影响黑色子树根节点的权值,对于黑色子树的其他节点没有影响,所以黑色子树的节点的权值和为 0 + ( − 1 ) = − 1 0+(-1)=-1 0+(1)=1,同理右边情况红色子树节点权值和为 1 1 1

当去掉一个节点时,由于该节点的颜色是确定的,因此与其连接的节点的颜色也是确定的(若去掉的节点为红色,则所有与之相连的节点为黑色,反之亦然),因此与其连接的子树的权值和是确定的,要么为 1 1 1,要么为 − 1 -1 1,所以去掉该节点后剩下的连通块(即子树)的权值即为确定的。

当然不想看证明的模拟一下就行了,而且真实比赛时也不可能想到这种证明的。
下面放上样例的模拟(绿色为权值):
CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes)_第2张图片

代码

#include 

// #define int long long

using namespace std;

constexpr int P = 998244353;
using i64 = long long;

void solve()
{
	int n;
	cin >> n;

	vector<vector<int>> a(n);
	vector<int> d(n);

	for (int i = 0; i < n - 1; i ++ ) {
		int u, v;
		cin >> u >> v;
		u -- ;
		v -- ;
		d[u] ++ ;
		d[v] ++ ;
		a[u].push_back(v);
		a[v].push_back(u);
	}

	vector<int> ans(n);
	
	// u为当前遍历节点,fa为u的父亲,主要是防止往回搜,t为节点类型,方便赋权值
	function<void(int, int, int)> dfs = [&](int u, int fa, int t) {
		ans[u] = t * d[u]; // 权值由度数和颜色决定
		for (int i = 0; i < (int)a[u].size(); i ++ ) {
			int v = a[u][i];
			if (v == fa) continue;
			dfs(v, u, -t);
		}
	};
	dfs(0, -1, 1);

	for (int i = 0; i < n; i ++ ) cout << ans[i] << " ";
	cout << "\n";
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int T;
	cin >> T;

	while (T -- ) {
		solve();
	}

	return 0;
}

你可能感兴趣的:(codeforces日常,算法)