ABC229 覆盤
[ABC229C] Cheese
思路解析
題目已經告訴了你每克比薩能帶來的美味度,因此直接以每克的美味度為關鍵字貪心即可。
時間複雜度:一次排序,\(O(n \log n)\)。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<long long, long long>
#define fir first
#define sec second
const int N = 3e5 + 10;
int n, w;
PII c[N];
bool cmp(PII x, PII y) {
return x.fir > y.fir;
}
signed main() {
cin >> n >> w;
for(int i = 1; i <= n; i++) {
cin >> c[i].fir >> c[i].sec;
}
sort(c + 1, c + n + 1, cmp);
int sum = 0, ans = 0;
for(int i = 1; i <= n; i++) {
if(sum + c[i].sec <= w) {
ans += c[i].fir * c[i].sec;
sum += c[i].sec;
}
else {
ans += c[i].fir * (w - sum);
break;
}
}
cout << ans;
return 0;
}
[ABC229D] Longest X
思路解析
雙指標,我們可以用一個 \(l,r\) 表示我們當前列舉到的區間,也就是我們想讓整個區間都為 \(\texttt{X}\)。\(r\) 一直右移直到 \(l,r\) 之間的 \(\texttt{.}\) 的個數超過 \(k\),此時 \(r-l\) 就是這個區間的長度,答案就是所有這種區間長度的最大值,以及每一次我們擴充完 \(r\) 後 \(l\) 也要右移。
時間複雜度:雙指標,\(l,r\) 都最多走 \(n\) 步,\(O(n)\)。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
string str;
int k, s[N];
int main() {
cin >> str >> k;
int n = str.size();
str = ' ' + str;
for(int i = 1; i <= n; i++) {
s[i] = s[i - 1];
if(str[i] == '.') s[i]++;
}
int l = 1, r = 0, ans = -1;
while(l <= n) {
while(s[r] - s[l - 1] <= k && r <= n) r++;
ans = max(ans, r - l);
l++;
}
cout << ans;
return 0;
}
[ABC229E] Graph Destruction
思路解析
題目要求刪點,而眾所周知刪點的代價要大於加點的代價,於是我們考慮倒著處理詢問,將每一個刪點改成加點,而加點時就用並查集維護連通塊即可。
時間複雜度:由於需要遍歷到每一條邊,並查集還有一個小常數,\(O(mw)\),\(w\) 為常數。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, fa[N];
vector<int> g[N];
int find(int x) {
if(x == fa[x]) return x;
return (fa[x] = find(fa[x]), fa[x]);
}
int main() {
cin >> n >> m;
for(int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = 1; i <= n; i++) fa[i] = i;
int ans = 0;
stack<int> st;
for(int i = n; i >= 1; i--) {
st.push(ans);
ans ++;
for(auto it : g[i]) {
if(it > i) {
if(find(it) != find(i)) ans --;
fa[find(it)] = find(i);
}
}
}
while(!st.empty()) cout << st.top() << '\n', st.pop();
return 0;
}
CF118E Bertown roads
思路解析
看到題目將無向邊轉成有向邊求強連通分量就能想到基本是 tarjan 的板子。可以發現題目要求的其實就是橋(割邊,也就是刪掉該邊後整張圖不連通),若有橋,則說明不可能滿足題目的條件;若沒有橋,則邊的方向就是 tarjan 遍歷到的方向。所有條件都可以透過 tarjan 的邊雙連通分量求得,因此跑一遍 tarjan 即可。
code
#include<bits/stdc++.h>
using namespace std;
#define PII pair<int, int>
#define sec second
#define fir first
const int N = 1e5 + 10, M = 3e5 + 10;
int n, m, dfn[N], low[N], idx = 0, cnt = 0;
struct node {
int to, nxt;
} g[M << 1];
int head[M << 1], ect = 1;
void add(int u, int v) {
g[++ect].to = v;
g[ect].nxt = head[u];
head[u] = ect;
}
vector<int> dcc[N];
bool vis[N], ve[M << 1], brg;
stack<int> st;
vector< PII > ans;
void tarjan(int u) {
if(dfn[u]) return;
dfn[u] = low[u] = ++idx;
st.push(u);
for(int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if(ve[i]) continue;
ve[i] = ve[i ^ 1] = true;
ans.push_back({u, v});
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
if(dfn[u] < low[v]) brg = true;
}
else low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
int v = -1; cnt++;
while(v != u) {
v = st.top(); st.pop();
dcc[cnt].push_back(v);
}
}
}
int main() {
memset(head, -1, sizeof(head));
cin >> n >> m;
for(int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
add(u, v); add(v, u);
}
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
if(brg) return (int)(cout << 0, 0);
for(auto it : ans) cout << it.fir << ' ' << it.sec << '\n';
return 0;
}