2020 高階競程 Contest #7 - 題解
- Task Provider: raiso
- Task Setter: raiso
首殺 team20_23 (00:06)
解法說明
這題就是一棵樹去遍歷,以 開始當作樹根,找到每個節點的父節點,就結束了。
這題的考點還有一個就是會使用輸入輸出加速,這樣你就成功了!
標準程式
- Task Provider: Ian
- Task Setter: Ian
首殺 team20_18 (00:07)
解法說明
簡單來說是做前綴和,在提示中有提到 xor 其實是在數某個 bit 的 1 到底出現過奇數次還是偶數次,也就是說 xor 運算有各種有的沒的交換結合率,並且 也就是說可以透過再 xor 運算一次逆運算出 的結果
定義
其實就等於 也就是消去掉 的部分
標準程式
- Task Provider: MiohitoKiri5474
- Task Setter: MiohitoKiri5474
首殺 team20_23 (01:24)
解法說明
次小,代表權重和為第二小
所以觀察一下剩下還沒有用到的邊,檢查這個邊加進去現有的 MST 中,所形成的環拿掉最小的邊的權重和,是否為除了 MST 以外、最小的生成樹
所以步驟會像是這樣:
- 做 MST
- 對 MST 做倍增(或剖分,反正能用來找 LCA 的東西皆可)
- 查詢沒有用到的點鐘, 的最小值
另外 是代表在 MST 上, 的路徑上所經過的邊的最大值
dp 表單的 first 就是存標準倍增法的節點
而 second 是存點 到第 個祖先之間,以及 的祖先到 之間,最大的邊
所以倍增轉移式如下
標準程式
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define INF 0x3f3f3f3f
#define maxN 200005
#define maxLog 20
typedef pair < int, int > pii;
#define F first
#define S second
struct bri{
int u, v, w;
};
inline bool cmp ( bri a, bri b ){
return a.w < b.w;
}
vector < bri > edges;
vector < pii > mst[maxN];
int ma, dis[maxN], n, D[maxN];
pii dp[maxN][maxLog];
vector < int > LOG;
inline void Init ( void ){
for ( int i = 0 ; i < maxN ; i++ )
dis[i] = i;
}
inline int find ( int d ){
return dis[d] == d ? d : dis[d] = find ( dis[d] );
}
inline void Union ( int a, int b ){
dis[find ( a )] = find ( b );
}
inline bool same ( int a, int b ){
return find ( a ) == find ( b );
}
inline void dfs ( int d, int p, int dep ){
D[d] = dep++;
dp[d][0].F = p;
for ( auto i: mst[d] ){
if ( i.F == p )
continue;
dp[i.F][0] = pii ( d, i.S );
dfs ( i.F, d, dep );
}
}
inline void buildLCA ( void ){
memset ( dp, -1, sizeof dp );
memset ( D, 0, sizeof D );
dfs ( 0, -1, 0 );
for ( int k = 1 ; k < maxLog ; k++ ){
for ( int i = 0 ; i < n ; i++ ){
if ( dp[i][k - 1].F == -1 )
continue;
dp[i][k].F = dp[dp[i][k - 1].F][k - 1].F;
dp[i][k].S = max ( dp[i][k - 1].S, dp[dp[i][k - 1].F][k - 1].S );
}
}
}
inline void findLCA ( int x, int y ){
if ( D[x] < D[y] )
swap ( x, y );
for ( int i = maxLog - 1 ; i >= 0 ; i-- ){
if ( dp[x][i].F != -1 && D[dp[x][i].F] >= D[y] ){
ma = max ( ma, dp[x][i].S );
x = dp[x][i].F;
}
}
if ( x == y )
return;
for ( int i = maxLog - 1 ; i >= 0 ; i-- ){
if ( dp[x][i].F != dp[y][i].F ){
ma = max ( ma, max ( dp[x][i].S, dp[y][i].S ) );
x = dp[x][i].F, y = dp[y][i].F;
}
}
ma = max ( ma, max ( dp[x][0].S, dp[y][0].S ) );
}
int main(){
ios::sync_with_stdio ( false );
cin.tie ( 0 );
cout.tie ( 0 );
int m, t = 1, ans, swp = 0, u, v, w;
vector < int > unUsed;
for ( int i = 0 ; i < maxLog ; i++ ){
LOG.pb ( t );
t <<= 1;
}
t = 1;
cin >> n >> m;
Init();
ans = INF;
ma = -1;
for ( int i = 0 ; i < m ; i++ ){
cin >> u >> v >> w;
edges.pb ( bri { u, v, w } );
}
sort ( edges.begin(), edges.end(), cmp );
for ( int i = 0 ; i < m ; i++ ){
if ( same ( edges[i].u, edges[i].v ) ){
unUsed.pb ( i );
continue;
}
Union ( edges[i].u, edges[i].v );
mst[edges[i].u].pb ( pii ( edges[i].v, edges[i].w ) );
mst[edges[i].v].pb ( pii ( edges[i].u, edges[i].w ) );
swp += edges[i].w;
}
buildLCA();
for ( auto i: unUsed ){
ma = -1;
findLCA ( edges[i].u, edges[i].v );
ans = min ( ans, edges[i].w - ma );
}
cout << ans + swp << '\n';
}
- Task Provider: ys
- Task Setter: ys
首殺 team20_05 (00:16)
解法說明
- 令 為 個人過來前,桌子上目前佔最多人的人數
- 令 為題目要求的最多人的那桌的最小解
若先去坐 人的那個桌子, 會變大,所以 個人應先分配到其他桌子上
於是求除了 人的桌子外,還剩下多少位置(rem
)
若 則顯然
若 就將部份人分配到剩下的位置中(此時桌子人數都為 ),
接著將剩餘的人 平均分配到每張桌子上,
標準程式
- Task Provider: leo900807
- Task Setter: leo900807
首殺 – (-:-)
解法說明
Subtask 1
暴力匹配,每架飛機都匹配一個機場,看是否能降落,共 種可能,最後取 。
Subtask 2
將每架飛機與機場,視為二分圖兩邊的點,對於每架飛機,將其與其航程內所有機場連邊,最後對這張圖做最大匹配即可。
標準程式
- Task Provider: ys
- Task Setter: ys
首殺 team20_12 (02:17)
解法說明
顯然這是一種找最短路徑的問題,當 時,直接用 BFS 算出起點到終點的最短路徑。
而 時,一個位置 可在 秒內飛到 遠的位置 :
這樣複雜度為 ,若飛行途中都沒遇到障礙物,則會 TLE
仔細觀察同個方向飛行時無障礙物的狀況,
若下個位置 到達時間較小,那麼從 開始飛會比較省時,
於是同個方向的搜尋可提早結束:
複雜度為
標準程式
- Task Provider: TIOJ 2005
- Task Setter: leo900807
首殺 – (-:-)
解法說明
Subtask 1
不會超出 long long 範圍,直接做就好。
Subtask 2
由題目敘述可以導出遞迴式
那就直接 做,要記得 。
Subtask 3
這種線性遞迴的題目,就會想到可以用矩陣快速冪來優化,但是 該如何處理呢?
不妨將其拆成 並分開進行矩陣快速冪,就能將 視為常數,而矩陣的形式如下:
那麼
因此一開始若將 設為 ,那麼
接著只要用上述的方法分段快速冪就可以得到答案了,要記得過程中都要
標準程式
看起來很亂,其實主要都是矩陣運算,實際上程式碼不長