1. 程式人生 > 實用技巧 >Codeforces Raif Round 1 (Div. 1 + Div. 2)

Codeforces Raif Round 1 (Div. 1 + Div. 2)

A

#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
typedef double db;
 
template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b ? (a = b, true) : false; }
template<class T1, class T2> bool umax(T1& a, T2 b) { return a < b ? (a = b, true) : false; }
template<class T> void clear(T& a) { T().swap(a); }
 
const int N = 2e5 + 5;
 
int n, m, _, k;
ll a[N], b[N];
 
int main() {
    IOS;
    for (cin >> _; _; --_) {
        ll a, b, x, y; cin >> a >> b >> x >> y;
        ll ans = abs(x - a) + abs(y - b) + 2;
        if (x == a || y == b) ans -= 2;
        cout << ans << '\n'; 
    } 
    return 0;
}

B

char s[N];
 
int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> s + 1;
        int a = 0;
        unordered_set<int> st;
        rep (i, 1, n) {
            if (s[i] == '-') st.insert(i % n), st.insert((i + 1) % n);
            else if (s[i] == '>') {
                if (a == 0) a = 1;
                else if (a == 2) a = -1;
            } else {
                if (a == 0) a = 2;
                else if (a == 1) a = -1;
            }
        }
        if (a != -1) cout << n << '\n';
        else cout << st.size() << '\n';
    }
    return 0;
}

C

貪心模擬

int main() {
    IOS;
    for (cin >> _; _; --_) {
        string s; cin >> s;
        int x = 0, y = 0, ans = 0;
        bool f = 0;
        for (auto c : s) {
            if (c == 'A') {
                if (f && y) ++x, y = 0, f = 0;
                ++x;
            }
            else if (c == 'B') {
                if (x) --x, ++ans, f = 1;
                else {
                    ++y;
                    if (y == 2) ++ans, y = 0;
                }
            }
        }
        cout << s.size() - (ans << 1) << '\n';
    } 
    return 0;
}

D

考慮到後效性, 倒著放, 同時對於行, 也是從 n 到 1, 把後效性消掉

我用的set只是為了圖形優美, 什麼容器都可

int a[N];
VI ans[N];
set<PII> st;
set<int> t;
 
int main() {
    IOS; cin >> n; int res = 0; m = n;
    rep(i, 1, n) cin >> a[i];
    bool f = 1;
    per(i, n, 1) {
        if (a[i] == 0) continue;
        if (a[i] == 1) ans[i].pb(m), ++res, st.insert({ m--, i });
        else if (a[i] == 2) {
            if (st.empty()) { f = 0; break; }
            PII ls = *st.rbegin();
            ans[i].pb(ls.fi); ++res;
            st.erase(ls); t.insert(i);
        }
        else {
            if (st.empty() && t.empty()) { f = 0; break; }
            if (!t.empty()) {
                int ls = *t.begin();
                ans[i].pb(m); ++res;
                ans[ls].pb(m--); ++res;
                t.erase(ls); t.insert(i);
            }
            else {
                PII ls = *st.rbegin();
                ans[i].pb(m); ++res;
                ans[ls.se].pb(m--); ++res;
                st.erase(ls); t.insert(i);
            }
        }
    }
    if (!f || m < 0 || res > 2 * n) { cout << -1; return 0; }
    cout << res << '\n';
    rep(i, 1, n)
        for (auto j : ans[i]) {
            if (j < 0) cout << j - k + 1 << ' ';
            else cout << j - k << ' ';
            cout << i << '\n';
        }
    return 0;
}

E

優先佇列

注意到, 將一個胡蘿蔔分的快數越多, 花費的時間越少, 就發現是優先佇列, 每次選取 多分出一塊 省的時間最多的那一根胡蘿蔔就行

ll a[N], b[N];

ll get(int x, int k) {
	if (a[x] < k) return 1e9;
	ll siz = a[x] / k, res = a[x] % k;
	return sqr(siz + 1) * res + sqr(siz) * (k - res);  
}

int main() {
    IOS; cin >> n >> k;
	priority_queue<PLI> q;
	rep (i, 1, n) {
		cin >> a[i], b[i] = 1;
		q.push({ get(i, 1) - get(i, 2), i });
	}
	rep (i, n + 1, k) {
		int x = q.top().se; q.pop();
		++b[x]; q.push({ get(x, b[x]) - get(x, b[x] + 1), x });
	}
	ll ans = 0;
	rep (i, 1, n) ans += get(i, b[i]);
	cout << ans;
    return 0;
}

F

//下標從1開始

dp(線段樹也可), 常數大的O(n)

g[i] 表示 j~i (1 <= j <= i) 對答案的貢獻, 那麼最後的答案就是 \(\sum g[i]\)

明顯 s[i] == '0', g[i] == g[i - 1],

對於 s[i] == '1', 討論要複雜一些

因為是考慮區間最長的 '1', 理所應當我們要存個 t[i], 表示以 i 結尾的最長 '1', 畢竟在遍歷 i 的時候, 區間最長 '1' 是會變的

顯然 if s[i] == '0', t[i] = 0; else t[i] = t[i-1] + 1

當從 i - 1 到 i 的時候最後一段 '1' 變長了(s[i] == '1') 也就是說, 在上一段最後 t[i]個字元 到 (i - 1) - t[i - 1] 原本最長段是 t[i - 1](t[i - 1] == t[i] - 1)

然而現在成了 t[i], 也就說在這個區間內 多貢獻了個區間長度, 對於 i-t[i]~i 也是多貢獻了個區間長度

設 f[i] 表示 上一個長度為 i 的 '1' 串的起始位置, "1101" 在對於第4個位置的時候, 當前 '1' 段為 [4,4], 那麼上一個 長度為 1 的段 是 [2, 2], 長度為2的段時[1, 2], 故 f[1] = 2, f[2] = 2

那麼 s[i] == '1' 轉移就已經出來了 g[i] = g[i + 1] + (i - t[i] - f[t[i]]) + t[i];

那麼現在的問題成了怎麼求 f[i], 顯然 f[i] 對於 '1' 段是倒著的, "111110", 對於6位置 f[1] = 5, f[2] = 2, f[3] = 3, f[4] = 4, f[5] = 5;

其實已經發現了 f[i] = 上一個串的長度 - 正著數的位置, 比如對於 位置 i,

f[length(包含i的最長 '1' 的長度) - t[i] + 1] = i

至於長度嘛, 預處理O(n), 就行了, 雖然我用的並查集, 但還是O(1)的合併, 直接和 '1' 段的起始座標最為祖先

然後我們又發現個問題, f[i] 隨著 i 更新, 但我們計算 g[i] 的時候是想要未更新之前的 f[i], 如果先轉移完, 在取跟新 f[i], 那必定超時(每遇到一個 '1' 串, 你都要重新遍歷一遍)

所以每個位置存兩個 f[i] 就好了, 當前的和上一個的, 通過下標判斷, 應該取哪個和更新哪個

這樣 f[i] 我們也有了

總的複雜度 O(n), 常數可能有點大, 並查集這裡直接是O(1)

#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef pair<ll, int> PLI;
typedef vector<int> VI;
typedef double db;

template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b ? (a = b, true) : false; }
template<class T1, class T2> bool umax(T1& a, T2 b) { return a < b ? (a = b, true) : false; }
template<class T> void clear(T& a) { T().swap(a); }

const int N = 5e5 + 5;

int n, m, _, k;
int fa[N], siz[N];
ll g[N], t[N];
PII f[N];
char s[N];

int find(int x) {
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void unit(int x, int y) {
	x = find(x); fa[y] = x; siz[x] += siz[y];
}

int get(int x, int k) {
	if (f[k] == PII{ 0, 0 }) return 0;
	if (f[k].se > f[k].fi) swap(f[k].fi, f[k].se);
	if (fa[f[k].fi] != fa[x]) return f[k].fi;
	return f[k].se;
}

void change(int x, int k) {
	if (f[k].fi == 0) f[k].fi = x;
	else if (f[k].se == 0) f[k].se = x;
	else if (f[k].fi > f[k].se) f[k].se = x;
	else f[k].fi = x;
}

int main() {
	IOS; cin >> n >> s + 1;
	rep(i, 1, n) fa[i] = i;
	rep(i, 1, n)
		if (s[i] == '1') {
			t[i] = t[i - 1] + 1; siz[i] = 1;
			if (s[i - 1] == '1') unit(i - 1, i);
		}

	rep(i, 1, n) {
		if (s[i] == '0') g[i] = g[i - 1];
		else {
			g[i] = g[i - 1] + i - t[i] - get(i, t[i]) + t[i];
			int a = siz[find(i)]; change(i, a - t[i] + 1);
		}
	}
	ll ans = 0;
	rep(i, 1, n) ans += g[i];
	cout << ans;
	return 0;
}