【LGR-109】洛谷 5 月月賽 II & Windy Round 6
排名
(見證歷史)
A P8344 「Wdoi-6」走在夜晚的蓮臺野
顯然有解要滿足 \(x \leq z\) 否則無解.
再考慮最多有多少個銀色木板能被放入:
顯然我們一定要先放銀色, 再放金色(否則先放的金色會被浪費, 沒有利用).
於是我們可以貪心地每次都放滿銀色, 再預留一個位置給即將放的金色. 具體地:
第 \(1\) 次操作: 可以放入 \(z - 1\) 個銀色, 再放入一個金色, 取出 \(z - 1\) 個銀色 (最後 \(1\) 個位置留給金色)
第 \(2\) 次操作: 可以放入 \(z - 2\) 個銀色, 再放入一個金色, 取出 \(z - 2\) 個銀色 (前面已經有了 \(1\)
第 \(3\) 次操作: 可以放入 \(z - 3\) 個銀色, 再放入一個金色, 取出 \(z - 3\) 個銀色 (前面已經有了 \(2\) 個金色, 再預留 \(1\) 個位置)
......
第 \(x\) 次操作: 可以放入 \(z - x\) 個銀色, 再放入一個金色, 取出 \(z - x\) 個銀色 (前面已經有了 \(z - x - 1\) 個金色, 再預留 \(1\) 個位置)
最後一個操作後還剩下 \(z - x\) 個位置, 此時金色已經用完, 可以全部放銀色.
故對於 \(y\) 需要滿足 \(y \leq (z - 1) + (z - 2) + .. + (z - x) + (z - x)\)
即 \(y \leq \frac{(2z - x - 1)x}{2} + z - x\)
注意 \(1 \leq y \leq 5 \times 10^{17}\) , 所以要開 long long
.
時間複雜度: \(O(1)\)
#include <bits/stdc++.h> #define int long long using namespace std; inline int read() { int x = 0; char c = getchar(); bool f = 1; while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return (f ? x : -x); } int t, x, y, z; signed main() { t = read(); while(t--) { x = read(); y = read(); z = read(); if(x <= z && y <= (2 * z - x - 1) * x / 2 + z - x) puts("Renko"); else puts("Merry"); } return 0; }
B P8345 「Wdoi-6」華胥之夢
假設當前我們第 \(i\) 個走過的點為 \(p_i\) 那麼據題意, 有:
\(length = \sum _{i = 1} ^{|s| - 1} (a[p_i] - 2a[p_{i + 1}] + c)\)
\(\ \ \ \ \ \ \ \ \ \ \ = (|s| - 1)c + \sum _{i = 1} ^{|s - 1|} a[p_i] - 2 \sum _{i = 1} ^{|s - 1|} a[p_{i + 1}]\)
\(\ \ \ \ \ \ \ \ \ \ \ = (|s| - 1)c + \sum _{i = 1} ^{|s - 1|} a[p_i] - 2 \sum _{i = 2} ^{|s|} a[p_{i}]\)
\(\ \ \ \ \ \ \ \ \ \ \ = (|s| - 1)c + (\sum _{i = 2} ^{|s - 1|} a[p_i] + a[p_i]) - (2 \sum _{i = 2} ^{|s - 1|} a[p_{i}] + 2a[p_{|s|}])\)
\(\ \ \ \ \ \ \ \ \ \ \ = (|s| - 1)c + a[p_i] - 2a[p_{|s|}] + \sum _{i = 2} ^{|s - 1|} a[p_i] - 2 \sum _{i = 2} ^{|s - 1|} a[p_{i}]\)
\(\ \ \ \ \ \ \ \ \ \ \ = (|s| - 1)c + a[p_i] - 2a[p_{|s|}] - \sum _{i = 2} ^{|s - 1|} a[p_i]\)
\(\ \ \ \ \ \ \ \ \ \ \ = (|s| - 1)c + 2a[p_i] - a[p_{|s|}] - \sum _{i = 1} ^{|s|} a[p_i]\)
推到這裡, 我們發現, 要使 \(length\) 最小, 只需要使 \(a[p_i]\) 最小, \(2a[p_{|s|}]\) 最大即可.
注意, 最終結果有可能爆 int
, 保險起見要開 long long
.
剩下的套公式就好了:
時間複雜度: \(O(n)\)
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int n, c, q, l, minv, maxv, a[N], s[N];
long long sum;
inline bool cmp(int &x, int &y) { return a[x] < a[y]; }
int main() {
n = read(); c = read(); q = read();
for(int i = 1; i <= n; ++i) a[i] = read();
while(q--) {
l = read();
sum = 0; minv = 0x3f3f3f3f; maxv = -0x3f3f3f3f;
for(int i = 1; i <= l; ++i) { s[i] = read(); sum += a[s[i]]; minv = min(minv, a[s[i]]); maxv = max(maxv, a[s[i]]); }
printf("%lld\n", ((long long)l - 1) * c + 2 * minv - maxv - sum);
}
return 0;
}
C P8346 「Wdoi-6」最澄澈的空與海
顯然有一個結論: 如果一個點只有一條邊與之相連, 那麼它只能和這條邊上另一點相連.
於是我們可以迴圈找這樣的點, 然後讓它與這條邊上另一點配對.
還有一種情況就是, 雖然這個點有很多邊, 但是其它的都已經配對過, 只剩下一個點沒有配對, 那麼也可以直接相連.
最後看所有點是否配對即可.
個人認為這題甚至比上一題簡單.
時間複雜度: \(O(n + m)\) (瞎推的, 有可能不對, 反正能過)
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e7 + 10;
int t, n, m, u, v, res, tmp;//res:目前配對個數 tmp:上一次配對個數
bool vis[N];//vis[i]:i是否配對
int main() {
t = read();
while(t--) {
n = read(); m = read();
res = 0;
vector<int> mp[N];
for(int i = 1; i <= (n << 1); ++i) vis[i] = 0;
for(int i = 1; i <= m; ++i) {
u = read(); v = read() + n;
mp[u].push_back(v);
mp[v].push_back(u);
}
do {
tmp = res;
for(int i = 1; i <= (n << 1); ++i) {
if(vis[i]) continue;
int pos = -1;
for(int j = 0; j < mp[i].size(); ++j) {
if(vis[mp[i][j]]) continue;
if(pos == -1) pos = j;
else { pos = -1; break; }
}
if(pos != -1) {//配對點唯一
vis[i] = vis[mp[i][pos]] = 1;//將這兩個配對
res += 2;
}
}
} while(tmp != res);//沒有新的配對了
if(res == (n << 1)) puts("Renko");
else puts("Merry");
}
return 0;
}
D P8347 「Wdoi-6」另一側的月
我們有一個非常簡單的結論, 當出現這種情況時, 顯然先手必勝:
Hifuu 可以取掉 \(1\) , 之後 Luna 怎麼取也會剩下一個單獨的節點.
我們可以發現 \(4\) 的度數為 \(2\) , \(7\) 的度數為 \(1\) .
於是我們發現 如果一個節點 \(u\) , 和它的兒子 \(v\) 滿足 \(in_u = 2, in_v = 1\) 則說明先手必勝.
再考慮擴廣: 如果一個節點 \(u\) , 和它的兒子 \(v\) 滿足 \(in_u = 2k, in_v = 1\) 則說明先手必勝.
想一下這麼一種情況:
如果 Hifuu 先手取 \(-1\) , 然後指定 \(0\) 這個連通塊:
顯然任何一個人都不會取掉 \(0\) (這樣直接就輸掉了), 於是他們只能交替選 \(1\) (Luna) \(2\) (Hifuu), Luna 最後只有 \(0, 3\) 可選, 但她無論選什麼都會輸, 所以可以簡單證明結論正確.
而如果你這麼寫的話會掛在 subtask 3 , 為什麼呢?
我們知道, 在這個子任務中他給定樹是完全二叉樹, 並且節點數等於 \(2^k - 1\) , 其實就是一個滿二叉樹最後一層少一個節點, 大概長這樣:
而對於這種情況, 顯然選擇 \(0\) 這個節點, 再選擇 \(2-5\) 這個連通塊, 則先手 Hifuu 必勝.
程式碼的話用 dfs
找 節點 \(u\) , 和它的兒子 \(v\) 滿足 \(in_u = 2, in_v = 1\) 即可.
時間複雜度: \(O(n)\)
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int t, n, u, v, in[N];
vector<int> mp[N];
bool res;
inline void dfs(int u, int fa) {
for(auto v : mp[u]) {
if(v == fa) continue;
if(!(mp[u].size() & 1) && mp[v].size() == 1) res = 1;
dfs(v, u);
}
}
int main() {
t = read();
while(t--) {
n = read(); res = 0;
for(int i = 1; i <= n; ++i) { in[i] = 0; mp[i].clear(); }
for(int i = 1; i < n; ++i) {
u = read(); v = read();
mp[u].push_back(v);
mp[v].push_back(u);
}
dfs(1, 0);
if(res || !((n + 1) & n)) puts("Hifuu");//或者滿二叉樹在最後一層少一個節點也是先手勝
else puts("Luna");
end: ;
}
return 0;
}
E P8348 「Wdoi-6」未知之花魅知之旅
這應該是一道結論題, 可惜我不會, 本來能拿 30 pts 的, 晚了 2s 交題. /ll
30 pts 爆搜卡常程式碼:
時間複雜度: \(O(3^n)\)
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
int t, a1, a2, x, y, k, l1, l2;
bool res, vis[1500][1500];
int main() {
t = read();
while(t--) {
a1 = read(); a2 = read(); x = read(); y = read(); k = read();
memset(vis, 0, sizeof(vis));
res = 0;
queue<pair<int, int>> q;
if(a1 >= k && a2 >= k) q.push({a1, a2});
while(q.size()) {
l1 = q.front().first; l2 = q.front().second; q.pop();
if(l1 >= 994 || l2 >= 994) continue;
vis[l1][l2] = 1;
if(l1 == x && l2 == y) { res = 1; break; }
if(l1 + l2 >= k && !vis[l2][l1 + l2]) { q.push({l2, l1 + l2}); }
if(l1 - l2 >= k && !vis[l2][l1 - l2]) { q.push({l2, l1 - l2}); }
if(l2 - l1 >= k && !vis[l2][l2 - l1]) { q.push({l2, l2 - l1}); }
}
if(res) puts("yes");
else puts("no");
}
return 0;
}