2020 高階競程 Contest #6 - 題解
- Task Provider: CF1355C
- Task Setter: raiso
首殺 team20_23 (00:12)
解法說明
首先我們要先知道構成三角形需要的條件就是「最長邊 其餘兩邊和」
這個兩邊和很重要,因為兩邊和代表一種壓縮的可能性。
誠如題目中的 Hint ,我們可以先找到短的兩邊和為 時,有幾種組裝可能性。
接者可以列舉所有的長邊值,統計選定長邊的情況下,有幾種短邊的組合。
之後後面有提供第二種做法,那是直接計算在選定長邊的情況下,有幾種短邊的組合。
標準程式
- Task Provider: CF1119E
- Task Setter: raiso
首殺 team20_18 (00:46)
解法說明
首先我們要先知道構成三角形需要的條件就是「最長邊 其餘兩邊和」
這次是告訴你,短邊與長邊一定差一倍,其實就是跟你說,不可能有兩短一長的方式能夠分組成功。
所以就只有三個同樣能力的可以分組成功,以及兩神帶一坑可以分組成功。
之後就是統計有幾個坑,讓神去帶,就可以分完了!
標準程式
- Task Provider: raiso
- Task Setter: raiso
首殺 team20_15 (00:24)
解法說明
使用拓展歐幾里德法,尋找整數對 使得 ,以及整數對 使得 ,之後判斷目標 是否為 的倍數,如果不是,就代表沒辦法組裝,如果是,那就把擴展歐幾里德的整數對放大幾倍,之後再用等於 的那組整數對加減使得 為最小非負整數。
標準程式
- Task Provider: ian
- Task Setter: ian
首殺 team20_46 (00:48)
解法說明
因為本題需求精度很高,推薦使用 python 內建大數作答,不過使用 c++ 還是可以通過的,python 的解答利用 pow(11,31) 來精確算出 pow(1.1,31) 的值,再利用 fake 算出假的值用以推算答案位數,並且由於 不可能為整數,所以直接將答案加一。
這裡提供第三筆測資,供各位參考測試
6
1970763
1981934
1977038
1674590
296173
1378417
標準程式
CPP 解
- Task Provider: leo900807
- Task Setter: leo900807
首殺 – (-:-)
解法說明
Subtask 1
暴力枚舉每個工作要不要做,要做就要上前面的實習課,共 種組合,每次 把要上的實習課的代價加總,最後用工作的報酬減掉代價,最後取 。
Subtask 2
因為至多參加一堂實習課,因此只要工作報酬比實習課的花費還多,或是工作不需要上實習課,就可以做那份工作,只要將這些工作報酬加總減掉實習課花費就能有最大獲利。
Subtask 3
因為實習課不重複,因此只要稍微擴展一下 Subtask 2 的算法即可。
Subtask 4
其實這題最後是決定要做哪些工作,因為要上哪些實習課是由做哪些工作決定,以題目敘述來說,要做 工作就需要參加 的實習課,這可以用一個有向圖來表示:
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
我們需要選擇圖上的一些點,使得這些被選到的點權和最大,但是在選擇點的時候有個條件,就是當一個點被選到時,其所有指到的點都要被選到。
這時我們就能使用最小割來解決這題,但是要經過一些轉化,先加上源點及匯點,以及將點權轉化為割的容量,如果我們要選擇一個點,則將其放在源點這端,否則放在匯點端,接著考慮頂點的選擇狀況:
- 頂點 被選到要花費 :
假設這個點被選到了,代表這個點在源點端,因此可以從這個點對匯點連接一條邊,容量為 ,如此一來,如果選到這個點,則需要花費 的代價,否則就不需要花費。
- 頂點 被選到會賺到 :
如果對匯點加一個容量為 的邊,會讓流量是負的,這樣不太好。我們可以換個方向想,假設我們已經賺到這個 了,那如果不選這個點需要花費 ,因此從源點對這個點連接一條邊,容量為 。
- 選頂點 不選頂點 的話會花費 :
從 對 連一條邊,容量為 ,如果 被選到且 沒被選到,則他們屬於不同的部分,因此會增加割的花費。
- 選頂點 一定要選頂點 :
這可以看成上面情況的特例,只要把 設成無限大就好。
題目經轉化會變成下圖
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
最後題目所求就變成「所有工作的報酬總和」減掉「最小割容量」,這題的最小割容量為 ,因此答案為 。
標準程式
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 4005, INF = 1e9;
struct MaxFlow{
struct edge{
int to, cap, flow, rev;
};
vector<edge> v[MAXN];
int s, t, dis[MAXN], cur[MAXN];
int dfs(int u, int cap){
if(u == t || !cap)
return cap;
for(int &i = cur[u]; i < v[u].size(); i++){
edge &e = v[u][i];
if(dis[e.to] == dis[u] + 1 && e.flow != e.cap){
int df = dfs(e.to, min(e.cap - e.flow, cap));
if(df){
e.flow += df;
v[e.to][e.rev].flow -= df;
return df;
}
}
}
dis[u] = -1;
return 0;
}
bool bfs(){
memset(dis, -1, sizeof(dis));
queue<int> q;
q.push(s), dis[s] = 0;
while(!q.empty()){
int tmp = q.front();
q.pop();
for(auto u : v[tmp]){
if(!~dis[u.to] && u.flow != u.cap){
q.push(u.to);
dis[u.to] = dis[tmp] + 1;
}
}
}
return dis[t] != -1;
}
int maxflow(int _s, int _t){
s = _s, t = _t;
int flow = 0, df;
while(bfs()){
memset(cur, 0, sizeof(cur));
while(df = dfs(s, INT_MAX))
flow += df;
}
return flow;
}
void add_edge(int st, int ed, int cap){
v[st].push_back(edge{ed, cap, 0, v[ed].size()});
v[ed].push_back(edge{st, 0, 0, v[st].size() - 1});
}
} dinic;
int main() {
int n, m, s, b, k, x, sum = 0;
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> s;
sum += s;
dinic.add_edge(0, i, s);
}
for(int i = 1; i <= m; i++){
cin >> b;
dinic.add_edge(i + 2000, 4004, b);
}
for(int i = 1; i <= n; i++){
cin >> k;
while(k--){
cin >> x;
dinic.add_edge(i, x + 2000, INF);
}
}
cout << sum - dinic.maxflow(0, 4004) << "\n";
}