Codeforces Round 983 div2 個人題解(A~D)
Dashboard - Codeforces Round 983 (Div. 2) - Codeforces
火車頭
#define _CRT_SECURE_NO_WARNINGS 1
#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <chrono>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
using namespace std;
#define ft first
#define sd second
#define yes cout << "yes\n"
#define no cout << "no\n"
#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define all1(x) x.begin() + 1, x.end()
#define unq_all(x) x.erase(unique(all(x)), x.end())
#define unq_all1(x) x.erase(unique(all1(x)), x.end())
#define sort_all(x) sort(all(x))
#define sort1_all(x) sort(all1(x))
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3fLL
#define RED cout << "\033[91m" // 紅色
#define GREEN cout << "\033[92m" // 綠色
#define YELLOW cout << "\033[93m" // 藍色
#define BLUE cout << "\033[94m" // 品紅
#define MAGENTA cout << "\033[95m" // 青色
#define CYAN cout << "\033[96m" // 青色
#define RESET cout << "\033[0m" // 重置
template <typename T>
void Debug(T x, int color = 1)
{
switch (color)
{
case 1:
RED;
break;
case 2:
YELLOW;
break;
case 3:
BLUE;
break;
case 4:
MAGENTA;
break;
case 5:
CYAN;
break;
default:
break;
}
cout << x;
RESET;
}
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
// typedef __int128_t i128;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pdd;
typedef pair<ll, int> pli;
typedef pair<string, string> pss;
typedef pair<string, int> psi;
typedef pair<string, ll> psl;
typedef tuple<int, int, int> ti3;
typedef tuple<ll, ll, ll> tl3;
typedef tuple<ld, ld, ld> tld3;
typedef vector<bool> vb;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<string> vs;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef vector<pli> vpli;
typedef vector<pss> vpss;
typedef vector<ti3> vti3;
typedef vector<tl3> vtl3;
typedef vector<tld3> vtld3;
typedef vector<vi> vvi;
typedef vector<vl> vvl;
typedef queue<int> qi;
typedef queue<ll> ql;
typedef queue<pii> qpii;
typedef queue<pll> qpll;
typedef queue<psi> qpsi;
typedef queue<psl> qpsl;
typedef priority_queue<int> pqi;
typedef priority_queue<ll> pql;
typedef map<int, int> mii;
typedef map<int, bool> mib;
typedef map<ll, ll> mll;
typedef map<ll, bool> mlb;
typedef map<char, int> mci;
typedef map<char, ll> mcl;
typedef map<char, bool> mcb;
typedef map<string, int> msi;
typedef map<string, ll> msl;
typedef map<int, bool> mib;
typedef unordered_map<int, int> umii;
typedef unordered_map<ll, ll> uml;
typedef unordered_map<char, int> umci;
typedef unordered_map<char, ll> umcl;
typedef unordered_map<string, int> umsi;
typedef unordered_map<string, ll> umsl;
std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
template <typename T>
inline T read()
{
T x = 0;
int y = 1;
char ch = getchar();
while (ch > '9' || ch < '0')
{
if (ch == '-')
y = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * y;
}
template <typename T>
inline void write(T x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x >= 10)
{
write(x / 10);
}
putchar(x % 10 + '0');
}
/*#####################################BEGIN#####################################*/
void solve()
{
}
int main()
{
ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
// freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
int _ = 1;
std::cin >> _;
while (_--)
{
solve();
}
return 0;
}
/*######################################END######################################*/
// 連結:
A. Circuit
Alice 剛剛製作了一個帶有 \(n\) 個燈和 \(2n\) 個開關的電路。每個元件(燈或開關)都有兩種狀態:開或關。燈和開關的排列方式如下:
每個燈連線到恰好兩個個開關。
每個開關連線到恰好一個燈。不知道每個開關連線到哪個燈。
當所有開關都關閉時,所有燈也會關閉。
如果切換開關(從開到關,或反之亦然),則連線到它的燈的狀態也會切換。
Alice 把只顯示 \(2n\) 個開關狀態的電路帶給她的妹妹 Iris,並給了她一個謎語:可以開啟的燈的最小和最大數量是多少?
Iris 非常瞭解她妹妹的滑稽動作,她只用了一秒鐘就給了 Alice 一個正確的答案。你能做到同樣的事情嗎?
輸入
每個測試由多個測試用例組成。第一行包含一個整數 \(t\) (\(1 \leq t \leq 500\)) — 測試用例的數量。測試用例的描述如下。
每個測試用例的第一行包含一個整數 \(n\) (\(1 \leq n \leq 50\)) — 電路中的燈的數量。
每個測試用例的第二行包含 \(2n\) 個整數 \(a_1,a_2,…,a_{2n}\) (\(0 \leq a_i \leq 1\)) — 電路中開關的狀態。 \(a_i=0\) 表示第 \(i\) 個開關關閉, \(a_i=1\) 表示第 \(i\) 個開關開啟。
輸出
對於每個測試用例,輸出兩個整數——分別表示可以開啟的燈的最小數量和最大數量。
示例
輸入
5
1
0 0
1
0 1
1
1 1
3
0 0 1 0 1 0
3
0 1 1 1 0 0
輸出
0 0
1 1
0 0
0 2
1 3
提示
在第一個測試用例中,電路中只有一個燈,且沒有開關開啟,因此燈肯定是關閉的。
在第二個測試用例中,電路中只有一個燈,但與之連線的一個開關是開啟的,因此燈是開啟的。
在第三個測試用例中,電路中只有一個燈,且兩個開關都是開啟的,因此燈是關閉的,因為它被切換了兩次。
在第四個測試用例中,為了沒有燈開啟,開關可以這樣排列:
開關 \(1\) 和開關 \(4\) 連線到燈 \(1\)。由於兩個開關都關閉,燈 \(1\) 也關閉。
開關 \(2\) 和開關 \(6\) 連線到燈 \(2\)。由於兩個開關都關閉,燈 \(2\) 也關閉。
開關 \(3\) 和開關 \(5\) 連線到燈 \(3\)。兩個開關都是開啟的,因此燈 \(3\) 被切換了兩次,從初始關閉狀態保持關閉。
而為了開啟 \(2\) 個燈,開關可以這樣排列:
開關 \(1\) 和開關 \(2\) 連線到燈 \(1\)。由於兩個開關都關閉,燈 \(1\) 也關閉。
開關 \(3\) 和開關 \(4\) 連線到燈 \(2\)。由於開關 \(3\) 是開啟的而開關 \(4\) 是關閉的,燈 \(2\) 從初始關閉狀態被切換了一次,因此它是開啟的。
開關 \(5\) 和開關 \(6\) 連線到燈 \(3\)。由於開關 \(5\) 是開啟的而開關 \(6\) 是關閉的,燈 \(3\) 從初始關閉狀態被切換了一次,因此它是開啟的。
解題思路
讀題可知一個01
對應一個開著的燈,而00
和11
對應著關著的燈。
所以如果1
為偶數,則最小開燈數為\(0\),1
和0
內部之間互相匹配,否則為\(1\)。
最大開燈數則為\(\min(\text{cnt0},\text{cnt1})\)
程式碼實現
void solve()
{
int n;
cin >> n;
vi a(n * 2);
int cnt0 = 0;
int cnt1 = 0;
for (int i = 0; i < n * 2; i++)
{
cin >> a[i];
if (a[i] == 0)
cnt0++;
else
cnt1++;
}
cout << (cnt1 & 1) << " " << min(cnt0, cnt1) << endl;
}
B. Medians
給定一個陣列 $ a = [1, 2, \ldots, n] $,其中 $ n $ 為奇數,以及一個整數 $ k $。
您的任務是選擇一個奇數正整數 $ m $,並將 $ a $ 拆分為 $ m $ 個子陣列 $ b_1, b_2, \ldots, b_m $,使得:
- 陣列 $ a $ 的每個元素都只屬於一個子陣列。
- 對於所有 $ 1 \leq i \leq m $, $ |b_i| $ 為奇數,即每個子陣列的長度為奇數。
- $ \text{median}([\text{median}(b_1), \text{median}(b_2), \ldots, \text{median}(b_m)]) = k $,即所有子陣列中位數的陣列中位數必須等於 $ k \(。\) \text{median}(c) $ 表示陣列 $ c $ 的中位數。
輸入
每個測試由多個測試用例組成。第一行包含一個整數 $ t $ ($ 1 \leq t \leq 5000 $)——測試用例數。測試用例說明如下。
每個測試用例的第一行包含兩個整數 $ n $ 和 $ k $ ($ 1 \leq k \leq n < 2 \cdot 10^5 $,且 $ n $ 為奇數)——陣列 $ a $ 的長度和所有子陣列中位數陣列的期望中位數。
保證所有測試用例中 $ n $ 的總和不超過 $ 2 \cdot 10^5 $。
輸出
對於每個測試用例:
如果沒有合適的分割槽,則單行輸出 \(-1\)。
否則,在第一行輸出一個奇數整數 $ m $ ($ 1 \leq m \leq n $),在第二行輸出 $ m $ 個不同的整數 $ p_1, p_2, p_3, \ldots, p_m $ ($ 1 = p_1 < p_2 < p_3 < \ldots < p_m \leq n $)——表示每個子陣列的左邊界。具體來說,對於有效答案 $ [p_1, p_2, \ldots, p_m] $:
- $ b_1 = [a_{p_1}, a_{p_1 + 1}, \ldots, a_{p_2 - 1}] $
- $ b_2 = [a_{p_2}, a_{p_2 + 1}, \ldots, a_{p_3 - 1}] $
- \(\ldots\)
- $ b_m = [a_{p_m}, a_{p_m + 1}, \ldots, a_n] $
如果有多個解決方案,您可以輸出其中任何一個。
示例
輸入
4
1 1
3 2
3 3
15 8
輸出
1
1
3
1 2 3
-1
5
1 4 7 10 13
提示
在第一個測試用例中,給定的分割槽有 $ m = 1 $ 且 $ b_1 = [1] $。顯然 $ \text{median}([\text{median}([1])]) = \text{median}([1]) = 1 $。
在第二個測試用例中,給定的分割槽有 $ m = 3 $ 且:
- $ b_1 = [1] $
- $ b_2 = [2] $
- $ b_3 = [3] $
因此, $ \text{median}([\text{median}([1]), \text{median}([2]), \text{median}([3])]) = \text{median}([1, 2, 3]) = 2 $。
在第三個測試用例中,對於 $ k = 3 $ 並沒有有效的分割槽。
在第四個測試用例中,給定的分割槽有 $ m = 5 $ 且:
- $ b_1 = [1, 2, 3] $
- $ b_2 = [4, 5, 6] $
- $ b_3 = [7, 8, 9] $
- $ b_4 = [10, 11, 12] $
- $ b_5 = [13, 14, 15] $
因此, $ \text{median}([\text{median}([1, 2, 3]), \text{median}([4, 5, 6]), \text{median}([7, 8, 9]), \text{median}([10, 11, 12]), \text{median}([13, 14, 15])]) = \text{median}([2, 5, 8, 11, 14]) = 8 $。
解題思路
由於分割槽長度任意,我們考慮把\(k\)單獨拎出來放在最中間的分割槽,然後再左右兩側劃分相同數量的分割槽。
\(k\)為奇數,我們可以\([1,1][2,k-1][k,k][k+1,k+1][k+2,n]\)劃分。
\(k\)為偶數,我們可以\([1,k-1][k][k+1,n]\)劃分
還要特判一下\(k=1,k=n\)和\(k\)剛好為中位數的情況
程式碼實現
void solve()
{
ll n, k;
cin >> n >> k;
if (k == (n + 1) / 2)
{
cout << "1\n1\n";
return;
}
if (k == 1 || k == n)
{
cout << "-1\n";
return;
}
if (k & 1)
{
cout << "5\n";
cout << 1 << " " << 2 << " " << k << " " << k + 1 << " " << k + 2 << "\n";
}
else
{
cout << "3\n";
cout << 1 << " " << k << " " << k + 1 << "\n";
}
}
C. Trinity
您將獲得一個包含 $ n $ 個元素 $ a_1,a_2,…,a_n $ 的陣列 $ a $。
您可以執行以下操作任意次數(可能是 0):
選擇兩個整數 $ i $ 和 $ j $,其中 $ 1 \leq i,j \leq n $,並分配 $ a_i := a_j $。找出使陣列 $ a $ 滿足條件所需的最少運算元:
對於每個成對不同的索引三元組 \((x,y,z)\) ($ 1 \leq x,y,z \leq n $, $ x \neq y $, $ y \neq z $, $ x \neq z $),存在一個非退化三角形,其邊長為 $ a_x \(、\) a_y $ 和 $ a_z $,即 $ a_x + a_y > a_z \(、\) a_y + a_z > a_x $ 和 $ a_z + a_x > a_y $。
輸入
每個測試由多個測試用例組成。第一行包含一個整數 $ t $ ($ 1 \leq t \leq 10^4 $)——測試用例的數量。測試用例的描述如下。
每個測試用例的第一行包含一個整數 $ n $ ($ 3 \leq n \leq 2 \cdot 10^5 $)——陣列 $ a $ 中的元素數量。
每個測試用例的第二行包含 $ n $ 個整數 $ a_1,a_2,…,a_n $ ($ 1 \leq a_i \leq 10^9 $)——陣列 $ a $ 的元素。
保證所有測試用例的 $ n $ 之和不超過 $ 2 \cdot 10^5 $。
輸出
對於每個測試用例,輸出一個整數——所需的最少運算元。
示例
輸入
4
7
1 2 3 4 5 6 7
3
1 3 2
3
4 5 3
15
9 3 8 1 6 5 3 8 2 1 4 2 9 4 7
輸出
3
1
0
8
提示
在第一個測試用例中,可以進行以下操作:
- 賦值 $ a_1 := a_4 = 4 $,陣列變為 $ [4,2,3,4,5,6,7] $。
- 賦值 $ a_2 := a_5 = 5 $,陣列變為 $ [4,5,3,4,5,6,7] $。
- 賦值 $ a_7 := a_1 = 4 $,陣列變為 $ [4,5,3,4,5,6,4] $。
可以證明,最終陣列中任意三元組的元素都能形成一個非退化三角形,且不可能用少於 3 次操作得到答案。
在第二個測試用例中,我們可以賦值 $ a_1 := a_2 = 3 $,使陣列 $ a = [3,3,2] $。
在第三個測試用例中,由於 3、4 和 5 是有效的三角形邊長,因此不需要對陣列進行任何操作。
解題思路
觀察題目要求和三角形的性質發現,我們實際上是在求一個極差小於最小值的區間,即\(\min\times2>\max\)。
所以我們可以先給陣列排個序,列舉最小值,然後二分查詢最大值,我們就得到了所需的區間。
但是!!!
這種做法忽略了最小值只有一個的情況,如果最小值只有一個且次小值加最小值大於最大值,也是可行的。
所以,我們應該列舉最小值和次小值,然後二分查詢小於他們之和的最大值,所找到的區間即為三角形可行區間。
區間外的元素就是我們需要操作的元素。
程式碼實現
void solve()
{
int n;
cin >> n;
vector<ll> a(n); // 注意a的範圍為1e9,最小值和次值相加時可能會溢位
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
sort(all(a));
int ans = inf;
for (int i = 1; i < n; i++)
{
int p = lower_bound(all(a), a[i] + a[i - 1]) - a.begin();
ans = min(ans, n - p + i - 1);
}
cout << ans << endl;
}
D. Genokraken
這是一個互動式問題。
清理完水邊區域後,Gretel 發現了一個名為 Genokraken 的怪物,她將其控制起來用於科學研究。
怪物的神經系統可以構造為一棵由 $ n $ 個節點組成的樹,編號從 $ 0 $ 到 $ n−1 $,以節點 $ 0 $ 為根。
Gretel 的目標是瞭解怪物神經系統的確切結構——更具體地說,她想知道樹的值 $ p_1,p_2,…,p_{n−1} $,其中 $ p_i $ ($ 0 \leq p_i < i $)是節點 $ i $ ($ 1 \leq i \leq n−1 $)的直接父節點。
她不知道節點的具體放置方式,但她知道一些方便的事實:
- 如果我們刪除根節點 $ 0 $ 和所有相鄰邊,這棵樹將變成僅由路徑組成的森林。最初與節點 $ 0 $ 相鄰的每個節點將成為某條路徑的終點。
- 節點的索引方式為,如果 $ 1 \leq x \leq y \leq n−1 $,則 $ p_x \leq p_y $。
- 節點 $ 1 $ 恰好有兩個相鄰節點(包括節點 $ 0 $)。
Gretel 可以對包含單元進行查詢:
“? a b”($ 1 \leq a,b < n \(,\) a \neq b $)——單元將檢查節點 $ a $ 和 $ b $ 之間的簡單路徑是否包含節點 $ 0 $。但是,為了避免過度刺激生物而導致意外後果,Gretel 最多希望查詢 $ 2n−6 $ 次。
輸入
每個測試由多個測試用例組成。第一行包含一個整數 $ t $ ($ 1 \leq t \leq 500 $)——測試用例的數量。測試用例的描述如下。
每個測試用例的第一行包含一個整數 $ n $ ($ 4 \leq n \leq 10^4 $)——Genokraken神經系統中的節點數。
保證所有測試用例的 $ n $ 之和不超過 $ 10^4 $。
互動
對於每個測試用例,互動從讀取整數 $ n $ 開始。
然後您可以進行以下型別的查詢:
“? a b”(不帶引號)($ 1 \leq a,b < n \(,\) a \neq b $)。查詢後,讀取一個整數 $ r $——查詢的答案。您最多可以使用 $ 2n−6 $ 個此類查詢。
- 如果節點 $ a $ 和 $ b $ 之間的簡單路徑不包含節點 $ 0 $,您將得到 $ r=0 $。
- 如果節點 $ a $ 和 $ b $ 之間的簡單路徑包含節點 $ 0 $,您將得到 $ r=1 $。
- 如果您進行了超過 $ 2n−6 $ 次查詢或進行了無效查詢,您將得到 $ r=−1 $。您需要在此之後終止以獲得“錯誤答案”判決。
當您找出結構時,以“! $ p_1 \ p_2 \ \ldots \ p_{n−1} $”(不帶引號)格式輸出一行,其中 $ p_i $ ($ 0 \leq p_i < i $)表示節點 $ i $ 的直接父節點的索引。此查詢不計入 $ 2n−6 $ 查詢限制。
解決一個測試用例後,程式應立即轉到下一個測試用例。解決所有測試用例後,程式應立即終止。
示例
輸入
3
4
1
5
1
0
9
輸出
? 2 3
! 0 0 1
? 2 3
? 2 4
! 0 0 1 2
! 0 0 0 1 3 5 6 7
提示
在第一個測試用例中,Genokraken 的神經系統形成了以下樹:
對“? 2 3”的答案是 $ 1 $。這意味著節點 $ 2 $ 和 $ 3 $ 之間的簡單路徑包含節點 $ 0 $。
在第二個測試用例中,Genokraken 的神經系統形成了以下樹:
對“? 2 3”的答案是 $ 1 $。這意味著節點 $ 2 $ 和 $ 3 $ 之間的簡單路徑包含節點 $ 0 $。
對“? 2 4”的答案是 $ 0 $。這意味著節點 $ 2 $ 和 $ 4 $ 之間的簡單路徑不包含節點 $ 0 $。
解題思路
說人話的題意:
給了你一棵特殊的樹,其節點編號為\(0\sim n-1\),根節點為\(0\)。
有3個性質
- 除了根節點外,其它節點最多隻有一個子節點。
- 如果節點\(u\)的編號小於\(v\)的編號,即\(u<v\),它們的父親\(p_u\)和\(p_v\)的關係為\(p_u\le p_v\)。
- \(1\)固定為根節點的一個子節點。
如果我們把根節點下的各個子樹稱為鏈,每次可以詢問\(a\)和\(b\)是否不在同一條鏈上
花費最多\(2n-6\)次詢問,找出除根以外的節點的父節點
思路
我們稱根節點的所有子節點為開始節點,可以得到三個推論:
-
開始節點是連續的,即開始節點的編號為\([1, m]\)。
假設其不連續,設不連續的那個點編號為\(x\),則$p_x \in [1,x-1] \(,可知\)p_{x+1}=0\(,則\)p_{x+1}\lt p_{x}$,違反了性質2
-
每條鏈上的點隨深度遞增的。
-
綜合上面兩個性質,我們可以得出,同一深度的點,所在鏈開始節點小的點的編號小於開始節點大的點的編號
據此,我們可以很輕易的想出詢問方式。
首先列舉\(2\sim n-1\)與\(1\)一起進行詢問,如果得到的是\(1\)說明該點也是開始節點。如果詢問\(1,m\)得到的是\(0\),說明開始節點為\([1,m-1]\),且由推論3可得\(p_m=1\)。
接著我們使用類似\(\text{bfs}\)的思路列舉剩下的節點\(i\in[m+1,n-1]\),我們設\(i\)可能的父節點為\(j\),\(j\)初始為2。如果詢問\(i,j\)得到的是\(0\),說明\(p_i=j\),\(i\)和\(j\)都要加一,因為一個節點最多隻能由一個子節點,否則\(j\)加一。
程式碼實現
int query(int a, int b)
{
printf("? %d %d\n", a, b);
fflush(stdout);
int x;
scanf("%d", &x);
return x;
}
void answer(const vector<int> &v)
{
printf("!");
for (int i = 1; i < v.size(); i++)
{
printf(" %d", v[i]);
}
printf("\n");
fflush(stdout);
}
void solve()
{
int n;
scanf("%d", &n);
vector<int> p(n);
int i = 1;
int m = 2;
while (query(1, m))
{
p[m] = 0;
m++;
}
p[m] = 1;
int j = 2;
for (int i = m + 1; i < n; i++)
{
while (query(i, j))
{
j++;
}
p[i] = j++;
}
answer(p);
}
這場掉大分。
B 題卡了好久,
C 題忘記考慮最小值只有一個的情況,連wa五發,
D 一開始沒看太明白題意,先去看E了
E 一開始以為是高斯消元求解線性方程組,設每個位置的操作次數都為\(x_i\),每個位置的值則為\(T=x_{i-1} +x_i*2+ x_{i+1} +a_i\),然後按這個思路搞了半天,突然發現時間複雜度不對。然後就一直在找快速求稀疏矩陣的方法,沒找到。賽後才發現不是求解線性方程組。
今天的題圖就選鈴蘭媽了,慶祝一下送的十連出了忍冬