https://hackmd.io/@omeletwithoutegg/TOI-2021-sols
https://www.facebook.com/notes/資訊競賽選手新手村/toi-2021/823380795202246/
因為測資不一定夠強,有些解也是賽後討論才AC或是甚至只有精神AC的
所以這裡的解有可能任何錯誤或是唬爛,不可全盤相信。
註:三模跟四模的 code 幾乎都沒有丟到師大給的臨時 judge 上跑,所以不保證正確。
定義一張圖的總花費是所有點對之間的最短距離總和。
給定一張
註:下面的code可能有些變數跟上面寫的不太一樣,例如
#pragma GCC optimize("Ofast", "unroll-loops")
#pragma loop_opt(on)
#include <bits/stdc++.h>
#ifdef local
#define debug(a...) qqbx(#a, a)
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = ("), ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#endif // local
using namespace std;
using ll = int64_t;
const int maxn = 505, inf = 1e9;
int dis[maxn][maxn];
int ans[maxn][maxn];
ll sum[maxn];
int cnt[maxn];
int n, m;
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (i != j)
dis[i][j] = inf;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
--a, --b;
dis[a][b] = dis[b][a] = 1;
}
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
for (int a = 0; a < n; a++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) sum[j] = cnt[j] = 0;
for (int b = 0; b < n; b++) {
int d = dis[a][b] - dis[i][b] - 1;
if (d < 0) continue;
cnt[d] += 1;
sum[d] += d;
/*
for (int j = 0; j < i; j++) {
ans[i][j] +=
max(0, dis[a][b] - dis[i][b] - 1 - dis[a][j]);
// \sum _ {d >= dis[a][j]} (d - dis[a][j])
// where d = dis[a][b] - dis[i][b] - 1
}
*/
}
for (int j = n-1; j >= 0; j--)
sum[j] += sum[j+1], cnt[j] += cnt[j+1];
for (int j = 0; j < i; j++) {
int d = dis[a][j];
ans[i][j] += sum[d] - cnt[d] * d;
}
}
}
ll mx = -1;
int cnt = 0;
for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) {
if (ans[i][j] == mx)
++cnt;
else if (ans[i][j] > mx)
mx = ans[i][j], cnt = 1;
}
cout << cnt << ' ' << mx << '\n';
}
/*
5 4
1 2
2 3
3 4
2 5
5 5
1 2
2 3
3 4
4 5
5 1
*/
給定
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define debug(a...) qqbx(#a, a)
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = ("), ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#endif // local
#define pb emplace_back
#define all(v) begin(v),end(v)
using namespace std;
using ll = int64_t;
const int maxn = 20005, maxm = 22, maxk = 500;
const int inf = 1e9;
void fail() {
cout << "impossible\n";
exit(0);
}
void add(int &x, int v) {
x = min(inf + 1, x + v);
}
int p2[maxn*2];
int dp[maxn][maxm][maxk];
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m, k, R;
assert( cin >> m >> n >> k >> R );
// while (k % 2 == 0);
p2[0] = 1;
for (int i = 1; i < maxn*2; i++) p2[i] = p2[i-1] * 2 % k;
dp[0][0][0] = 1;
for (int i = 0; i < maxn; i++) {
for (int j = 0; j < maxm; j++) {
for (int r = 0; r < k; r++) {
if (i+1 < maxn)
add(dp[i+1][j][r], dp[i][j][r]);
if (j+1 < maxm)
add(dp[i][j+1][(r + p2[i+j]) % k], dp[i][j][r]);
}
}
}
string ans;
if (m < maxm) {
int r = 0;
for (int i = 0; i < n+m; i++) r = (r + p2[i]) % k;
ans += '1';
--n;
if (dp[n][m][r] < R) fail();
while (n > 0 || m > 0) {
if (n > 0 && dp[n-1][m][r] >= R) {
ans += '1';
--n;
} else {
ans += '0';
if (n) R -= dp[n-1][m][r];
r = (r - p2[n+m-1] + k) % k;
--m;
}
}
} else {
int r = 0;
ans += '1';
r = (r - p2[n+m-1] + k) % k;
--n;
while (n >= maxm) {
ans += '1';
r = (r - p2[n+m-1] + k) % k;
--n;
}
if (dp[m][n][r] < R) fail();
while (n > 0 || m > 0) {
if (n > 0 && dp[m][n-1][(r-p2[n+m-1]+k)%k] >= R) {
ans += '1';
r = (r - p2[n+m-1] + k) % k;
--n;
} else {
ans += '0';
if (n) R -= dp[m][n-1][(r-p2[n+m-1]+k)%k];
--m;
}
}
}
cout << ans << '\n';
}
互動題,現在有一個遞迴數列形如
其中
另外有一個祕密的
現在給你history(x)
(必須滿足
同時你也可以詢問
假設你呼叫的 history
次數為
對於每個 subtask ,若你成功回答所有詢問,你的得分就是該子任務的分數,乘上那筆 subtask 所有測資中
subtask 1 (4)
subtask 2 (5)
subtask 3 (6)
subtask 4 (7)
subtask 5 (23)
subtask 6 (55)
subtask 1 ~ 4
注意到不管
要列出算式可以:
(有點唬爛的)滿分解
首先不知道
注意到,要確認是否是某種遞迴關係需要至少6項,正好可以問
接著
只用高斯消去
假設遞迴階數至多
#include <bits/stdc++.h>
#ifdef local
#define debug(a...) qqbx(#a, a)
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = ("), ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (auto it = L; it != R; ++it)
std::cerr << *it << ' ';
std::cerr << "]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define pb emplace_back
#define all(v) begin(v),end(v)
using namespace std;
using ll = long long;
const int MOD = 1000000007;
int history(int);
#ifdef local
namespace solution {
#endif // local
const int maxk = 10, maxn = 1000025;
vector<int> rec[4] = {
{2, 0, 1000000006}, // 1
// {3, 1000000006, 1000000005}, // 2^k
{1, 5, 1000000003, 1000000003}, // k&1 ? 2^k : 0
{7, 999999990, 14, 4, 999999999}, // k^2 * 2^k
{4, 1000000002, 1, 2, 1000000006}, // k^2
};
int fastLinearRecurrence(vector<int> init, vector<int> rel, long long n) {
vector<int> R{1}, E{0, 1};
auto mul = [&rel](vector<int> &a, const vector<int> &b) {
vector<int> c(a.size() + b.size() - 1);
for (size_t i = 0; i < a.size(); i++)
for (size_t j = 0; j < b.size(); j++)
c[i+j] = (c[i+j] + 1LL * a[i] * b[j]) % MOD;
for (size_t i = c.size()-1; i >= rel.size(); i--)
for (size_t j = 0; j < rel.size(); j++)
c[i-j-1] = (c[i-j-1] + 1LL * rel[j] * c[i]) % MOD;
c.resize(rel.size());
a = c;
};
while (n) {
if (n & 1)
mul(R, E);
mul(E, E);
n >>= 1;
}
int sum = 0;
for (size_t i = 0; i < R.size(); i++)
sum = (sum + 1LL * init[i] * R[i]) % MOD;
return sum;
}
int predict(ll t) {
const int k = 6;
vector<int> h(k);
for (int i = 0; i < k; i++) h[i] = history(i);
auto match = [&](vector<int> rel) {
vector<int> v = h;
if (rel.size() * 2 < v.size()) v.resize(rel.size() * 2);
reverse(all(v));
pary(all(v));
pary(all(rel));
for (int i = v.size()-1, cnt = 0; i >= (int)rel.size(); i--) {
ll sum = 0;
for (size_t j = 0; j < rel.size(); j++) {
sum = (sum + 1LL * rel[j] * v[i-j-1]) % MOD;
}
if (sum != v[i])
return debug(i, sum, v[i], rel.size()), false;
}
return true;
};
for (int i = 0; i < 4; i++) {
vector<int> v = h;
v.resize(rec[i].size());
reverse(all(v));
pary(all(v));
debug(fastLinearRecurrence(v, rec[i], i+v.size()-1));
if (match(rec[i]))
return fastLinearRecurrence(v, rec[i], t+v.size()-1);
}
return -1;
}
#ifdef local
}
const int maxn = 1000000;
int val[maxn];
int pw[maxn];
int start, A, B;
int history(int z) {
if (z > start) return 0;
return val[start - z];
}
signed main() {
cin >> start >> A >> B;
val[0] = A;
val[1] = A+B;
pw[0] = 1;
for (int i = 1; i < maxn; i++) pw[i] = pw[i-1] * 2 % MOD;
for (int i = 2; i < maxn; i++) val[i] = 1LL*i*i%MOD*pw[i]%MOD;
for (int i = 2; i < maxn; i++)
val[i] = (0LL + val[i] + val[i-1] + val[i-2]) % MOD;
cout << solution::predict(1) << '\n';
debug(history(-1));
}
#endif // local
有
現在依序發射了
subtask 1 (5)
subtask 2 (7)
subtask 3 (15)
subtask 4 (25)
subtask 5 (48) 無額外限制 (
vector<int> seg[10001]
存每個X座標的目標物編號,接著每次發射砲彈就把那一格嘗試pop
出東西來。vector
之類的存。那麼要查一次射擊會打到誰,就是看那個葉子到根的路徑,Y座標最小而且還沒被射掉的目標物是誰了。deque
有點肥,因此使用deque
、stack
、queue
都有可能MLE。第一種解
note: not verified
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
#define debug(a...) qqbx(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = (") , ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L)
std::cerr << (f++ ? ", " : "") << *L;
std::cerr << " ]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
#define pb emplace_back
#define get_pos(u,x) int(lower_bound(all(u),x)-begin(u))
using namespace std;
using ll = int_fast64_t;
using ld = long double;
template <typename T> using min_heap = priority_queue<T, vector<T>, greater<T>>;
const int mod = 1000000007;
const int inf = 1e9;
const ll INF = 1e18;
const int maxn = 500002;
bool removed[maxn];
struct Segtree {
int n;
vector<int> st[maxn * 4];
void init(int _n) {
n = _n;
}
void add(int l, int r, int id) {
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) st[l++].push_back(id);
if (r & 1) st[--r].push_back(id);
}
}
int query(int p) {
int res = -1;
for (p += n; p; p >>= 1) {
while (!st[p].empty() && removed[st[p].back()])
st[p].pop_back();
res = max(res, st[p].empty() ? -1 : st[p].back());
}
return res;
}
} sgt;
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m;
cin >> n >> m;
vector<tuple<int,int,int>> seg(n);
vector<int> xs(m), u;
for (auto &[s, t, w]: seg)
cin >> s >> t >> w, ++t, u.pb(s), u.pb(t);
for (int &x: xs) cin >> x;
u.insert(u.end(), all(xs));
sort(all(u)), u.erase(unique(all(u)), u.end());
sgt.init(u.size());
for (int i = 0; i < n; i++) {
auto [s, t, w] = seg[i];
s = get_pos(u, s);
t = get_pos(u, t);
sgt.add(s, t, i);
}
ll ans = 0;
for (int x: xs) {
x = get_pos(u, x);
int id = sgt.query(x);
if (id != -1) {
debug(id);
auto [s, t, w] = seg[id];
ans += w;
removed[id] = true;
}
}
cout << ans << '\n';
}
第二種解
#include <bits/stdc++.h>
#ifdef local
#define debug(a...) qqbx(#a, a)
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = ("), ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#endif // local
#define pb emplace_back
#define all(v) begin(v),end(v)
using namespace std;
using ll = int64_t;
const int maxn = 505, inf = 1e9;
struct Segtree {
int n;
vector<int> mn;
Segtree(int sz, function<int(int)> v) : n(sz), mn(sz*2) {
for (int i = 0; i < n; i++) mn[i+n] = v(i);
for (int i = n-1; i > 0; i--) mn[i] = min(mn[i<<1], mn[i<<1|1]);
}
int queryMin(int l, int r) {
int res = inf;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) res = min(res, mn[l++]);
if (r & 1) res = min(res, mn[--r]);
}
return res;
}
void edit(int p, int v) {
for (mn[p+=n] = v; p>1; p>>=1)
mn[p>>1] = min(mn[p], mn[p^1]);
}
};
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m;
cin >> n >> m;
vector<tuple<int,int,int>> seg(n);
vector<int> xs(m), u;
for (auto &[s, t, w]: seg) cin >> s >> t >> w, ++t, u.pb(s), u.pb(t);
for (int &x: xs) cin >> x;
u.insert(u.end(), all(xs));
sort(all(u)), u.erase(unique(all(u)), u.end());
vector<multiset<int>> ms(u.size());
for (int i = 0; i < m; i++) {
int x = lower_bound(all(u), xs[i]) - u.begin();
ms[x].insert(i);
xs[i] = x;
}
Segtree sgt(u.size(), [&ms](int i){ return ms[i].empty() ? inf : *ms[i].begin(); });
reverse(all(seg));
ll ans = 0;
for (auto &[s, t, w]: seg) {
s = lower_bound(all(u), s) - u.begin();
t = lower_bound(all(u), t) - u.begin();
int id = sgt.queryMin(s, t);
if (id == inf) continue;
ans += w;
ms[xs[id]].erase(id);
sgt.edit(xs[id], ms[xs[id]].empty() ? inf : *ms[xs[id]].begin());
}
cout << ans << '\n';
}
A和B在玩拿石頭的遊戲,共有
首先A可以任選一堆,並從該堆取至少一個,至多
並且除了第一輪A取石頭以外,取的石頭數量都不能比對手多,但至少要取一個石頭。
拿到最後一個石頭的人就勝利了,問A第一次取石頭有幾種方法可以讓A必勝?
subtask 1 (24)
subtask 2 (13)
subtask 3 (12)
subtask 4 (23)
subtask 5 (28) 無額外限制
感謝 wiwiho 讓我參考 code
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe std::cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename ...T> void qqbx(const char *s, T ...args) {
int cnt = sizeof...(T);
((cerr << "\e[1;32m(" << s << ") = ("), ..., (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
cerr << "\e[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
using namespace std;
int lowbit(int x) {
return x & -x;
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, R;
cin >> n >> R;
vector<int> a(n);
for (int i = 0; i < n; i++)
cin >> a[i];
int xor_sum = 0;
for (int i = 0; i < n; i++)
xor_sum ^= a[i];
if (xor_sum == 0 || lowbit(xor_sum) > R)
return cout << 0 << '\n', 0;
int ans = 0;
for (int i = 0; i < n; i++) {
int newA = a[i];
for (int j = 0; j < 30; j++) {
// assume 1 << j as highbit of R'
// lowbit(xor_sum ^ a[i] ^ (a[i] - R')) > R'
if ((xor_sum ^ a[i] ^ newA) >> j & 1) {
if (newA < (1 << j))
break;
newA -= 1 << j;
}
int newR = a[i] - newA;
if (newR > 0 && newR <= R && (int)__lg(newR) == j) {
++ans;
}
}
}
cout << ans << '\n';
}
有
另外有
subtask 1 (17)
subtask 2 (29)
subtask 3 (28) 所有操作的
subtask 4 (26) 無額外限制
這樣計算的複雜度大概是
// #define _GLIBCXX_DEBUG
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
#define debug(a...) qqbx(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = (") , ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L)
std::cerr << (f++ ? ", " : "") << *L;
std::cerr << " ]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
#define pb emplace_back
using namespace std;
using ll = int64_t;
template <typename T> using max_heap = priority_queue<T, vector<T>, less<T>>;
const int maxn = 100025;
const long long inf = 1000000001;
const int prs[4] = {2, 3, 5, 7};
int bp[1 << 4];
int square_free(int x) {
for (int p: prs)
while (x % (p*p) == 0)
x /= p;
return x;
}
int mul(int a, int b) {
return min(inf, 1LL*a*b);
}
int cnt[101];
int calc(int L) {
vector<int> has;
for (int j = 1; j <= 100; j++) if (cnt[j]) has.push_back(j);
int ans = 0;
for (int s = 0, U = (1<<4)-1; s < (1<<4); s++) {
bool fail = false;
int prod = 1;
bool inProd[100] = {};
for (int j: has) {
bool coprime = (__gcd(j, bp[U ^ s]) == 1);
if (coprime) {
int bigPart = j / __gcd(j, 2 * 3 * 5 * 7);
if (bigPart == 1) {
prod = inf;
break;
} else {
if (!inProd[bigPart])
prod = mul(prod, bigPart);
inProd[bigPart] = true;
}
}
}
int M = L / bp[U ^ s] / prod;
if (M == 0) continue;
for (int m = s; ; m = (m-1) & s) {
int coef = __builtin_parity(m) ? -1 : 1;
ans += coef * (M / bp[m]);
if (!m) break;
}
}
#ifdef local
int c = 0;
for (int i = 1; i <= L; i++) {
bool ok = true;
for (int j: has)
if (__gcd(j, i) == 1)
ok = false;
if (ok)
++c;
}
pary(all(has));
debug(ans, L, c);
#endif // local
return ans;
}
signed main() {
for (int s = 0; s < (1<<4); s++) {
bp[s] = 1;
for (int i = 0; i < 4; i++) if (s >> i & 1) bp[s] *= prs[i];
}
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m;
assert( cin >> n >> m );
vector<pair<int,int>> evt;
for (int i = 0; i < m; i++) {
int l, r, x;
assert( cin >> l >> r >> x );
assert( 1 <= l && l <= r && r <= n );
assert( 1 <= x && x <= 100 );
x = square_free(x);
--l;
evt.emplace_back(l, x);
evt.emplace_back(r, -x);
}
evt.emplace_back(0, 0);
evt.emplace_back(n, 0);
sort(all(evt));
int ans = 0;
for (int i = 0, j = 0; i < evt.size(); i = j) {
for (j = i; j < evt.size(); j++) {
if (evt[j].first != evt[i].first) break;
int x = evt[j].second;
if (x > 0) {
++cnt[x];
} else if (x < 0) {
--cnt[-x];
}
}
if (j == evt.size()) continue;
int l = evt[i].first;
int r = evt[j].first;
int cur = calc(r) - calc(l);
ans += cur;
debug(l, r, cur);
}
cout << ans << '\n';
}
/*
15 2
1 15 3
1 15 15
10000 2
1 10000 22
1 10000 33
*/
給定一個序列
一個點只能被匹配最多一次,當兩個點
此外,任何配對都不能出現部份相交的情形,也就是說,對於任兩個配對
匹配結束後,所有沒有被匹配到的點
最終得分就是所有選中的配對獲得的分數,與上述的額外分數的總和。
請找出最終得分最大是多少。
subtask 1 (9)
subtask 2 (16)
subtask 3 (33)
subtask 4 (42) 無額外限制
首先要注意題意的理解,因為範測實在蠻爛的。
原本筆者以為兩個配對的點中間不能再有其他配對的點,像是AI666那樣,但其實不是,題目的意思是兩組配對不能「交叉」,但可以完全包含在另一個配對中間,例如
subtask 2, 3
理解了正確的題意之後會發現這跟括弧匹配有點像,可以嘗試dp。應該有蠻多種方式去dp的,這裡列出
滿分解
如果對括弧匹配再仔細想的話,可以發現這題可以歸約成這題。
對於每個點,我可以計算他當左括弧的收益
兩種情況可以選價值比較大的那個,假如配起來的總價值是正的那我們就把他改成右括弧,否則就把他先當作未配對的點變成潛力左括弧,兩種情況都需要好好更新heap。如果再想細一點其實這兩個heap可以併在一起XD說明就略過了。
這個技巧被ZCK稱為可undo greedy,因為我們可以很輕鬆的把已經選的配對點拆開或是計算拆開之後減少的價值,所以就可以維護heap來決定要do誰跟undo誰。
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100025;
template <typename T> using max_heap = priority_queue<T, vector<T>, less<T>>;
int a[maxn];
long long pre[maxn];
signed main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) pre[i] = pre[i-1] + a[i];
max_heap<long long> pq;
long long ans = 0;
for (int i = 1; i <= n; i++) ans += max(a[i], 0);
for (int i = 1; i <= n; i++) {
long long lbraceValue = -pre[i-1] - max(a[i], 0);
long long rbraceValue = pre[i] - max(a[i], 0);
if (!pq.empty() && rbraceValue + pq.top() > 0) {
ans += rbraceValue + pq.top(), pq.pop();
pq.push(lbraceValue);
pq.push(-rbraceValue);
} else {
pq.push(lbraceValue);
}
}
cout << ans << '\n';
}
記憶體限制為 128 MB
二維平面上有
線段在下落的過程中保持斜率不變,故每個線段可以用三個參數
表示若在下落的過程中,左端點和右端點的座標分別是
則
所有點皆相異
subtask 1 (19)
subtask 2 (30)
subtask 3 (51) 無額外限制
這題的 subtask 切的蠻爛的@@
注意鉛直線的case最好小心處理,此外賽中subtask 1的測資沒有強到會驗出鉛直線的問題,許多人被這件事影響。
假設這題不是詢問線段而是詢問直線的話,那麼我們可以維護點的上凸包,每次詢問一條直線就相當於詢問凸包在某個方向的極點(extreme point),而這可以用三分搜解決,或是對詢問依照斜率排序之後用類似單調stack的方式解決。
現在詢問並不是一整條直線,而是
當然,如果想要空間
// #define _GLIBCXX_DEBUG
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
#define debug(a...) qqbx(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = (") , ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L)
std::cerr << (f++ ? ", " : "") << *L;
std::cerr << " ]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
#define pb emplace_back
using namespace std;
using ll = int64_t;
template <typename T> using max_heap = priority_queue<T, vector<T>, less<T>>;
const int maxn = 500025, maxq = 100025;
const ll INF = 3e18;
struct Point {
int x, y, id;
} p[maxn];
int u[maxn];
void buildConvexHull(vector<int> &v) {
static const auto check = [](int a, int b, int c) {
ll x1 = p[b].x - p[a].x, y1 = p[b].y - p[a].y;
ll x2 = p[c].x - p[b].x, y2 = p[c].y - p[b].y;
return x1 * y2 - x2 * y1 >= 0;
};
size_t j = 0;
for (size_t i = 0; i < v.size(); i++) {
while (j >= 2 && check(v[j-2], v[j-1], v[i]))
--j;
v[j++] = v[i];
}
v.resize(j);
}
tuple<ll,int,int> queryConvexHull(vector<int> &v, int w, int h) {
tuple<ll,int,int> ans(-INF, 0, -1);
if (v.empty()) return ans;
auto f = [&](int i) {
auto [x, y, id] = p[i];
return tuple<ll,int,int>(1LL * w * y - 1LL * h * x, x, id);
};
while (v.size() >= 2 && f(v.rbegin()[0]) <= f(v.rbegin()[1])) v.pop_back();
return f(v.back());
}
struct Segtree {
int n;
vector<int> st[maxn * 2];
void build(int _n) {
n = _n;
for (int i = 0; i < n; i++)
st[i+n].push_back(i);
for (int i = n-1; i > 0; i--) {
st[i].resize(st[i<<1].size() + st[i<<1|1].size());
merge(all(st[i<<1]), all(st[i<<1|1]), st[i].begin(), [](int a, int b){ return a < b; });
buildConvexHull(st[i]);
}
}
int query(int l, int r, int w, int h) {
tuple<ll,int,int> res(-INF, 0, -1);
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) res = max(res, queryConvexHull(st[l++], w, h));
if (r & 1) res = max(res, queryConvexHull(st[--r], w, h));
}
return get<2>(res);
}
} sgt;
struct Query {
int l, r, w, h;
int qid;
} qs[maxq];
int ans[maxq];
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> p[i].x >> p[i].y, p[i].id = i+1;
sort(p, p+n, [](Point a, Point b){ return a.x < b.x; });
{
int j = 0;
for (int i = 0; i < n; i++) {
if (!i || p[i].x != p[i-1].x) {
p[j++] = p[i];
} else if (p[j-1].y < p[i].y) {
p[j-1] = p[i];
}
}
n = j;
}
for (int i = 0; i < n; i++) u[i] = p[i].x;
sgt.build(n);
int q;
cin >> q;
for (int i = 0; i < q; i++) {
int a, w, h;
cin >> a >> w >> h;
int l = lower_bound(u, u+n, a) - u;
int r = lower_bound(u, u+n, a+w+1) - u;
qs[i] = { l, r, w, h, i };
}
sort(qs, qs+q, [](Query a, Query b) {
return 1LL * a.w * b.h > 1LL * a.h * b.w;
});
for (int i = 0; i < q; i++) {
auto [l, r, w, h, qid] = qs[i];
ans[qid] = sgt.query(l, r, w, h);
}
for (int i = 0; i < q; i++)
cout << ans[i] << '\n';
}
有排成一列的
要你選出
若有多組解請輸出字典序最小的解,無解則輸出
一組輸入檔案有
subtask 1 (16)
subtask 2 (41)
subtask 3 (43) 無額外限制
直接講滿分解。
考慮最左邊的花,也就是
如果不選
更一般地,假設目前已經選了一些花,那麼把不能選的花去除之後,最前面的那朵花和他的下一朵一定恰好會選一朵,因此這樣遞迴下去時間複雜度會類似
subtask 1 是直接枚舉
subtask 2 應該是給糟糕的
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe std::cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename ...T> void qqbx(const char *s, T ...args) {
int cnt = sizeof...(T);
((cerr << "\e[1;32m(" << s << ") = ("), ..., (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
cerr << "\e[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
using namespace std;
using ll = long long;
const int maxn = 1000025;
int color[maxn];
int nxt[maxn];
bool usedColor[50];
int curPos[50];
vector<int> ans;
int dfs_call;
bool dfs(int cs, int k) {
++dfs_call;
if (k == 0)
return true;
int pos1 = maxn-1;
for (int j = 0; j < cs; j++) {
if (!usedColor[j]) {
pos1 = min(pos1, curPos[j]);
}
}
if (pos1 == maxn-1)
return false;
int color1 = color[pos1];
usedColor[color1] = true;
int color2 = -1, color3 = -1;
int pos2 = -1, pos3 = -1;
for (int j = 0; j < cs; j++)
if (!usedColor[j] && curPos[j] == pos1 + 1) {
assert (color2 == -1);
color2 = j;
pos2 = curPos[color2];
}
if (color2 != -1) {
curPos[color2] = nxt[curPos[color2]];
}
if (dfs(cs, k-1)) {
ans.push_back(pos1);
return true;
}
if (color2 != -1) {
curPos[color2] = pos2;
}
usedColor[color1] = false;
if (color2 == -1)
return false;
usedColor[color2] = true;
assert (color1 != color2 && color2 != color3); // maybe color1 == color3
{
curPos[color1] = nxt[curPos[color1]];
}
for (int j = 0; j < cs; j++)
if (!usedColor[j] && curPos[j] == pos2 + 1) {
assert (color3 == -1);
color3 = j;
pos3 = curPos[j];
}
if (color3 != -1) {
curPos[color3] = nxt[curPos[color3]];
}
if (dfs(cs, k-1)) {
ans.push_back(pos2);
return true;
}
if (color3 != -1) {
curPos[color3] = pos3;
}
{
curPos[color1] = pos1;
}
usedColor[color2] = false;
return false;
}
int mp[maxn];
void solve() {
int n, k;
cin >> n >> k;
int tot = 0;
for (int i = 0; i < n; i++)
cin >> color[i];
for (int i = 1; i <= n; i++)
mp[i] = -7122;
for (int i = 0; i < n; i++) {
if (tot < k * 2 && mp[color[i]] == -7122) {
mp[color[i]] = tot++;
}
}
for (int j = 0; j < tot; j++)
curPos[j] = maxn-1, usedColor[j] = false;
nxt[maxn-1] = maxn-1;
for (int i = n-1; i >= 0; i--) {
if (mp[color[i]] >= 0) {
int id = mp[color[i]];
nxt[i] = curPos[id];
curPos[id] = i;
color[i] = id;
} else {
color[i] = -7122222;
}
}
ans.clear();
if (dfs(tot, k)) {
reverse(all(ans));
for (int i = 0; i < k; i++)
cout << ans[i]+1 << (i+1==k ? '\n' : ' ');
} else {
cout << 0 << '\n';
}
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int T;
cin >> T;
while (T--)
solve();
debug(dfs_call);
}
給定一棵
此外還會有
當沒有貓咪參加宴會時請輸出
樹的邊權介在
保證
而
subtask 1 (7)
subtask 2 (12) 任意時間點參加的人數只有
subtask 3 (22) 每個時間點參加宴會的人數總和
subtask 4 (24)
subtask 5 (35) 無額外限制
答案會在直徑上的證明:
首先假設有一條直徑的兩個端點分別是
接著如果
順帶一提筆者賽中是用這題的技巧動態維護樹直徑的,現在看起來超級中毒XD
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe std::cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename ...T> void qqbx(const char *s, T ...args) {
int cnt = sizeof...(T);
((cerr << "\e[1;32m(" << s << ") = ("), ..., (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
cerr << "\e[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
using namespace std;
using ll = long long;
const int maxn = 100025;
const int maxq = 200025;
vector<pair<int,int>> g[maxn];
int wsum[maxn], pa[20][maxn], dep[maxn];
void dfs(int i, int f) {
for (int L = 1; L < 20; L++)
pa[L][i] = pa[L-1][pa[L-1][i]];
for (auto [w, j]: g[i]) {
if (j == f) continue;
dep[j] = dep[i] + 1;
wsum[j] = wsum[i] + w;
pa[0][j] = i;
dfs(j, i);
}
}
int lca(int a, int b) {
if (dep[a] > dep[b]) swap(a, b);
int d = dep[b] - dep[a];
for (int i = 0; i < 20; i++)
if (d >> i & 1)
b = pa[i][b];
if (a == b) return a;
for (int i = 19; i >= 0; i--)
if (pa[i][a] != pa[i][b])
a = pa[i][a], b = pa[i][b];
return pa[0][a];
}
int dis(int a, int b) {
return wsum[a] + wsum[b] - 2 * wsum[lca(a, b)];
}
int solveTwoPoints(int a, int b, int d) {
if (a == -1 || b == -1)
return 0;
int c = lca(a, b);
if (wsum[a] - wsum[c] <= d / 2)
swap(a, b);
assert (wsum[a] - wsum[c] >= (d + 1) / 2);
int x = a;
for (int i = 19; i >= 0; i--)
if (dep[x] - dep[c] >= (1 << i) && wsum[a] - wsum[pa[i][x]] <= d / 2)
x = pa[i][x];
int ans = max(dis(a, x), dis(b, x));
if (x != c)
ans = min(ans, max(dis(a, pa[0][x]), dis(b, pa[0][x])));
return ans;
}
struct Diameter {
int a, b;
int d;
Diameter() : a(-1), b(-1), d(0) {}
bool add(int x) {
if (a == -1)
return a = x, true;
if (b == -1)
return b = x, d = dis(a, b), true;
if (int nd = dis(a, x); nd > d)
return b = x, d = nd, true;
if (int nd = dis(b, x); nd > d)
return a = x, d = nd, true;
return false;
}
};
struct Segtree {
vector<int> event[maxq * 2];
int n;
int ans[maxq];
void add(int l, int r, int e) {
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) event[l++].push_back(e);
if (r & 1) event[--r].push_back(e);
}
}
void dfs(int i, Diameter d) {
for (int x: event[i]) d.add(x);
if (i < n) {
dfs(i << 1, d);
dfs(i << 1 | 1, d);
} else {
ans[i - n] = solveTwoPoints(d.a, d.b, d.d);
}
}
} sgt;
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i < n; i++) {
int a, b, w;
cin >> a >> b >> w;
g[a].emplace_back(w, b);
g[b].emplace_back(w, a);
}
dfs(1, 0);
map<int,int> mp;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
mp[x] = 0;
}
int q;
cin >> q;
sgt.n = q + 1;
for (int i = 1; i <= q; i++) {
int p;
cin >> p;
assert (p != 0);
if (p > 0) {
assert (!mp.count(p));
mp[p] = i;
} else {
assert (mp.count(p));
p = -p;
sgt.add(mp[p], i, p);
mp.erase(p);
}
}
for (auto [p, t]: mp)
sgt.add(t, q+1, p);
sgt.dfs(1, Diameter());
for (int i = 0; i <= q; i++)
cout << sgt.ans[i] << '\n';
}
給定一張
另外有
假設走邊的時間不計,你想求「從頂點
保證可以從頂點
輸出浮點數與正確答案相對誤差
subtask 1 (9)
subtask 2 (49)
subtask 3 (23) 保證圖是 DAG
subtask 4 (19)
先考慮沒有加裝任何加速器的時候期望值是怎麼算的。
定義
那麼花費的總時間就是
那麼要如何求出
其中
仔細看 subtask 的話可以發現前三個 subtask 都是 DAG ,可以用 DP 的方式求出
接著是第二步,也就是如何加裝加速器。可以發現在同一個頂點裝加速器的效益是越來越低的,那麼有一個很直覺的貪心就是每次拿「能夠減少的時間」最多的頂點一直拿,因為如果不拿的話可以把最後一個裝的加速器直接換給那個頂點而使得最佳解至少不會變壞。
因為
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe std::cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename ...T> void qqbx(const char *s, T ...args) {
int cnt = sizeof...(T);
((cerr << "\e[1;32m(" << s << ") = ("), ..., (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
cerr << "\e[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
using namespace std;
using ld = long double;
const int maxn = 100025;
namespace gauss {
const ld eps = 1e-10;
ld A[105][105], b[105];
// Ax = b, solve x
void elim(int n) {
for (int k = 0; k < n; k++) {
if (abs(A[k][k]) <= eps) {
int p = -1;
for (int i = k+1; i < n; i++)
if (abs(A[i][k]) > eps) {
p = i;
break;
}
assert (p != -1);
for (int j = 0; j < n; j++)
swap(A[k][j], A[p][j]);
swap(b[k], b[p]);
}
for (int i = 0; i < n; i++) if (i != k) {
ld r = A[i][k] / A[k][k];
for (int j = 0; j < n; j++)
A[i][j] -= A[k][j] * r;
b[i] -= b[k] * r;
}
}
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (i != j)
assert (abs(A[i][j]) <= eps);
for (int i = 0; i < n; i++)
assert (abs(A[i][i]) > eps);
for (int i = 0; i < n; i++)
b[i] /= A[i][i];
}
}
int t[maxn];
int out[maxn];
vector<pair<int,int>> g[maxn];
int indeg[maxn];
ld E[maxn];
int rat[maxn];
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m, k;
cin >> n >> m >> k;
for (int i = 0; i < n; i++)
cin >> t[i];
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
--a, --b;
g[a].emplace_back(c, b);
out[a] += c;
indeg[b] += 1;
}
if (n > 100) {
E[0] = 1;
queue<int> que;
for (int i = 0; i < n; i++)
if (indeg[i] == 0) {
que.push(i);
}
assert (que.size() == 1 && que.front() == 0);
while (!que.empty()) {
int i = que.front(); que.pop();
for (auto [c, j]: g[i])
E[j] += E[i] * c / ld(out[i]);
for (auto [c, j]: g[i])
if (--indeg[j] == 0)
que.push(j);
}
for (int i = 0; i < n; i++)
assert (indeg[i] == 0);
} else {
for (int i = 0; i < n; i++)
gauss::A[i][i] = 1;
for (int i = 0; i < n; i++) {
// E[i] = [i=1] + \sum p_{j,i} E[j]
for (auto [c, j]: g[i]) {
ld p = c / ld(out[i]);
gauss::A[j][i] = -p;
}
}
gauss::b[0] = 1;
gauss::elim(n);
for (int i = 0; i < n; i++)
E[i] = gauss::b[i];
}
for (int i = 0; i < n; i++)
E[i] *= t[i];
priority_queue<pair<ld,int>> pq;
for (int i = 0; i < n; i++) {
rat[i] = 1;
pq.emplace(E[i] / rat[i] - E[i] / (rat[i] + 1), i);
}
for (int t = 0; t < k; t++) {
auto [_, i] = pq.top(); pq.pop();
rat[i] += 1;
pq.emplace(E[i] / rat[i] - E[i] / (rat[i] + 1), i);
}
ld ans = 0;
for (int i = 0; i < n; i++)
ans += E[i] / rat[i];
cout << fixed << setprecision(10);
cout << ans << '\n';
}
有
在滿足上述規則的同時,希望所有元件放置的
請輸出最小的
保證沒有兩條導線連接相同的兩個元件,並且每條導線的兩個端點是不同的元件。
subtask 1 (5)
subtask 2 (10)
subtask 3 (31)
subtask 4 (54) 無額外限制
其實看到題目給的附圖就會知道這題跟二分圖有關係了(?)
首先把元件當成點、導線當成邊,那麼題目等於是給定一張圖要你判他是不是同時是二分圖又可以照規則畫出來。
稍微畫幾個例子就可以知道,可以滿足規則的連通圖大概會是一條鏈加上一堆葉子。
可以用幾個規則來判這件事:首先圖要是樹,接著每個
另外,只要決定好哪些點要放上面哪些點要放下面的話,那麼最大的
因此我們相當於每個連通塊有兩種放法,讓某個點在上面還是下面會造成上面或是下面增加一定的點數。可以用背包問題類型的 DP 來解決這樣類似 0/1 背包問題的部份,但是複雜度可能有點不太對,有兩個方法可以解決並 AC 本題:
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe std::cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename ...T> void qqbx(const char *s, T ...args) {
int cnt = sizeof...(T);
((cerr << "\e[1;32m(" << s << ") = ("), ..., (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
cerr << "\e[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
using namespace std;
using ld = long double;
const int maxn = 100025;
bool vis[maxn];
vector<int> g[maxn];
bool color[maxn];
int cnt[2];
bitset<maxn> dp;
void dfs(int i, int f = -1) {
++cnt[color[i]];
vis[i] = true;
for (int j: g[i])
if (!vis[j]) {
color[j] = !color[i];
dfs(j, i);
} else if (j != f) {
// cycle = true;
cout << -1 << '\n';
exit(0);
}
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
--a, --b;
g[a].push_back(b);
g[b].push_back(a);
}
int sum = 0;
vector<int> diff;
for (int i = 0; i < n; i++)
if (!vis[i]) {
cnt[0] = cnt[1] = 0;
dfs(i);
sum += abs(cnt[0] - cnt[1]);
diff.push_back(abs(cnt[0] - cnt[1]));
}
for (int i = 0; i < n; i++)
if (g[i].size() > 1) {
int c = 0;
for (int j: g[i])
if (g[j].size() > 1)
++c;
if (c > 2) {
cout << -1 << '\n';
exit(0);
}
}
dp[0] = true;
for (int x: diff)
dp |= dp << x;
// dp[X] = we can make one side X
int ans = n;
for (int i = 0; i < maxn; i++)
if (dp[i]) {
// |a-b| = |i - (sum-i)|
int d = abs(sum - i * 2);
// a + b = n
assert (d % 2 == n % 2);
ans = min(ans, (n + d) / 2);
}
cout << ans << '\n';
}
註:四模打完沒有開放judge讓我們練習的時間,所以code跟賽中沒有人AC的cards不保證正確。
給定
對於第
每個凸多邊形的點數
頂點座標的絕對值
保證輸入給的頂點不會在凸多邊形邊上
subtask 1 (15) 保證輸入給的是平行座標軸的長方形
subtask 2 (11) 所有
subtask 3 (17) 對於每個多邊形皆有
subtask 4 (57) 無額外限制
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
#define debug(a...) qqbx(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = (") , ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L)
std::cerr << (f++ ? ", " : "") << *L;
std::cerr << " ]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
#define pb emplace_back
using namespace std;
using ll = long long;
using ld = long double;
const int inf = 1e9;
const ll INF = 1e18;
const int maxn = 300;
template <typename T, typename U>
bool chmin(T &t, const U &u) { return t < u ? false : (t=u, true); }
template <typename T, typename U>
bool chmax(T &t, const U &u) { return t > u ? false : (t=u, true); }
struct Vec {
int x, y;
void rot(Vec &v) {
int nx = v.y * x - v.x * y;
int ny = -(v.x * x + v.y * y);
x = nx, y = ny;
}
friend Vec operator-(const Vec &lhs, const Vec &rhs) {
return { lhs.x - rhs.x, lhs.y - rhs.y };
}
friend istream & operator>>(istream &I, Vec &v) {
return I >> v.x >> v.y;
}
ll norm() const {
return 1LL * x * x + 1LL * y * y;
}
};
using Polygon = vector<Vec>;
Vec v[maxn];
Polygon P[maxn];
ld intersection(Vec a, Vec b, int x, int init) {
if (a.x > b.x) swap(a, b);
if (x < a.x || x > b.x) return init;
if (a.x == b.x)
return abs(a.y - init) > abs(b.y - init) ? a.y : b.y;
return (1LL * a.y * (b.x - x) + 1LL * b.y * (x - a.x)) / ld(b.x - a.x);
}
pair<ld,ld> calc(Polygon A, Polygon B, Vec v) {
if (v.norm() != 0) {
for (auto &p: A) p.rot(v);
for (auto &p: B) p.rot(v);
// fix A, and rotate s.t. B moving down with velocity (0, -1)
}
vector<int> xs;
for (auto p: A) xs.push_back(p.x);
for (auto p: B) xs.push_back(p.x);
sort(all(xs)), xs.erase(unique(all(xs)), xs.end());
pary(all(xs));
ld lt = inf, rt = -inf;
for (int x: xs) {
ld la = inf, ra = -inf;
ld lb = inf, rb = -inf;
for (int i = 0; i < A.size(); i++) {
int j = i ? i-1 : A.size() - 1;
chmin(la, intersection(A[i], A[j], x, inf));
chmax(ra, intersection(A[i], A[j], x, -inf));
}
for (int i = 0; i < B.size(); i++) {
int j = i ? i-1 : B.size() - 1;
chmin(lb, intersection(B[i], B[j], x, inf));
chmax(rb, intersection(B[i], B[j], x, -inf));
}
chmin(lt, lb - ra);
chmax(rt, rb - la);
}
debug(lt, rt);
if (lt > rt || rt < 0)
return { inf, -inf };
if (v.norm() == 0)
return { -inf, inf };
if (lt < 0) lt = 0;
lt /= v.norm(), rt /= v.norm();
return { lt, rt };
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> v[i];
int sz;
cin >> sz;
P[i].resize(sz);
for (auto &p: P[i]) cin >> p;
}
cout << fixed << setprecision(10);
for (int i = 0; i < n; i++) {
vector<pair<ld,int>> evt;
for (int j = 0; j < n; j++) {
if (i != j) {
auto [l, r] = calc(P[i], P[j], v[j] - v[i]);
if (l < r) {
evt.emplace_back(l, 1);
evt.emplace_back(r, -1);
}
}
}
sort(all(evt));
int cnt = 0;
ld ans = 0;
for (int l = 0, r = 0; l < evt.size(); l = r) {
for (r = l; r < evt.size(); r++) {
if (evt[l].first != evt[r].first) break;
cnt += evt[r].second;
}
if (r == evt.size()) continue;
if (cnt) {
ans += evt[r].first - evt[l].first;
}
}
if (ans >= inf)
cout << "infinity\n";
else
cout << ans << '\n';
}
}
現在有一家餐廳,因為防疫關係只能做現點現做的外帶生意,所有等候外帶的顧客必須要在櫃台前排成一排,並且餐廳最多只能容納
subtask 1 (14)
subtask 2 (9)
subtask 3 (24)
subtask 4 (53) 無額外限制
先來喇分XD
multiset
維護隊伍中的客人,也可以用常數可能比較小的兩個heap。multiset
的過程後就AC了(雖然寫的有點醜)。
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
#define debug(a...) qqbx(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = (") , ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L)
std::cerr << (f++ ? ", " : "") << *L;
std::cerr << " ]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
#define pb emplace_back
#define get_pos(u,x) int(lower_bound(all(u),x)-begin(u))
using namespace std;
using ll = int_fast64_t;
using ld = long double;
template <typename T> using min_heap = priority_queue<T, vector<T>, greater<T>>;
const int mod = 1000000007;
const int inf = 1e9;
const ll INF = 1e18;
const int maxn = 5025;
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, k, s;
cin >> n >> k >> s;
vector<int> t(n), p(n);
{
vector<pair<int,int>> customer(n);
for (auto &[t, p]: customer)
cin >> t >> p;
sort(all(customer));
for (int i = 0; i < n; i++)
tie(t[i], p[i]) = customer[i];
}
vector<int> dp(n+1);
for (int i = 0; i < n; i++) {
dp[i+1] = max(dp[i+1], dp[i]);
int profit = p[i];
multiset<int> ms;
int j = i+1;
for (int x = 1; x <= n; x++) {
while (j < n && t[j] < t[i] + x * s) {
ms.insert(p[j++]);
while (ms.size() > k-1) ms.erase(ms.begin());
}
while (j < n && t[j] == t[i] + x * s) {
ms.insert(p[j++]);
while (ms.size() > k) ms.erase(ms.begin());
}
dp[j] = max(dp[j], dp[i] + profit);
if (ms.empty())
break;
profit += *prev(ms.end());
ms.erase(prev(ms.end()));
}
}
cout << dp[n] << '\n';
}
給定一棵
subtask 1 (13)
subtask 2 (19)
subtask 3 (7)
subtask 4 (9)
subtask 5 (52) 無額外限制
考慮枚舉樹上所有路徑,並計算有幾種路徑分割包含這條路徑。
從原本的樹上拔掉這條路徑的邊之後可能會剩下一些子樹,因此現在的問題就是如何計算一個樹有幾種路徑分割方法。一開始可能會想要用樹dp來計算,但其實每個頂點可以獨立計算,可以看成是每個頂點有一些相鄰的邊,並且邊可以任意兩兩配對或是不配對的方法數,也就是說一個頂點的貢獻只和他的degree有關。
事實上一棵樹的路徑分割方法數就是
比較有效率求出
可能會有人想要樹dp直接維護一個子樹的答案,但其實有時候把樹dp的維度弄太複雜反而不好實做也不好想,這個作法是從「每個路徑對答案貢獻多少」出發的。
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define pary(a...) danb(#a, a)
#define debug(a...) qqbx(#a, a)
template <typename ...T> void qqbx(const char *s, T ...a) {
int cnt = sizeof...(T);
((std::cerr << "\033[1;32m(" << s << ") = (") , ... , (std::cerr << a << (--cnt ? ", " : ")\033[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
std::cerr << "\033[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L)
std::cerr << (f++ ? ", " : "") << *L;
std::cerr << " ]\033[0m\n";
}
#else
#define debug(...) ((void)0)
#define safe ((void)0)
#define pary(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
#define pb emplace_back
#define get_pos(u,x) int(lower_bound(all(u),x)-begin(u))
using namespace std;
using ll = int_fast64_t;
using ld = long double;
template <typename T> using min_heap = priority_queue<T, vector<T>, greater<T>>;
const int mod = 1000000007;
const int inf = mod;
const ll INF = 1e18;
const int maxn = 5002;
ll dp[maxn], invdp[maxn];
ll modpow(ll e, ll p) {
ll r = 1;
while (p) (p&1) && (r=r*e%mod), e=e*e%mod, p>>=1;
return r;
}
vector<int> g[maxn];
int deg[maxn];
ll ans = 0;
void dfs(int i, int p, int dep, ll prod) {
ans = (ans +
1LL * dep * dep * prod % mod *
dp[deg[i]-1] % mod * invdp[deg[i]]) % mod;
if (p != -1 && g[i].size() >= 2) {
prod = prod *
dp[deg[i]-2] % mod * invdp[deg[i]] % mod;
}
for (int j: g[i]) {
if (j != p)
dfs(j, i, dep+1, prod);
}
}
ll fac[maxn], ifac[maxn], inv[maxn];
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
/*
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i < maxn; i++)
dp[i] = (dp[i-1] + dp[i-2] * (i-1)) % mod;
pary(dp, dp+10);
*/
inv[1] = 1;
for (int i = 2; i < maxn; i++)
inv[i] = inv[mod % i] * (mod - mod / i) % mod;
fac[0] = ifac[0] = 1;
for (int i = 1; i < maxn; i++) {
fac[i] = fac[i-1] * i % mod;
ifac[i] = ifac[i-1] * inv[i] % mod;
}
for (int i = 0; i < maxn; i++) {
dp[i] = 1;
ll prod = 1;
for (int j = 0; j * 2 + 2 \leq i; j++) {
ll C = fac[i - j*2] *
ifac[2] % mod * ifac[i - j*2 - 2] % mod;
prod = prod * C % mod;
dp[i] = (dp[i] + prod * ifac[j+1]) % mod;
}
}
pary(dp, dp+10);
for (int i = 0; i < maxn; i++)
invdp[i] = modpow(dp[i], mod-2);
int n;
cin >> n;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].pb(b);
g[b].pb(a);
++deg[a], ++deg[b];
}
ll prod = 1;
for (int i = 0; i < n; i++)
prod = prod * dp[deg[i]] % mod;
for (int i = 0; i < n; i++) {
debug(i);
dfs(i, -1, 0, prod *
dp[deg[i]-1] % mod * invdp[deg[i]] % mod);
}
cout << ans << '\n';
}
有
subtask 1 (12)
subtask 2 (31)
subtask 3 (57) 無額外限制
先考慮暴力做,就是每次合併完之後判斷這些敘述是否有可能同時達成。要怎麼判斷一堆敘述是否有可能同時達成呢?我們可以把每一個變數(
我認為如果可以的話,有時候把題目轉換成更具體的形式後有機會變得比較容易思考。這題的話可以把合併的過程想像成一顆有根的二元樹,其中的
再回頭看看第一段的做法,一個顯然會讓該作法的時間複雜度退化到
那如果合併的過程只是一般的二元樹呢?一個很單純的想法就是我們一次做完一條鏈,只要所有節點都包含於至少一條鏈中就做完整棵樹了。等等,鏈…樹…,樹鏈剖分?真是個自然的想法。所以我們就試著對那棵二元樹做樹鏈剖分,然後每一條重鏈也都會符合「存在一個分界點,使得它和它的子節點都是好的,而它的祖先都是壞的」,我們就一樣用二分搜的方式確定該條鏈上的節點是好是壞。
接著便是估複雜度的時候了!對於一條重鏈,我們所需要花的時間上面分析過,其實是
這只是個人大略的想法,一些其他問題,包括「一定要把樹建出來嗎?」、「要怎麼在重鏈上二分搜?」就希望大家可以想想看了?
by Omelet
過了好一陣子才把 code 補上,於是也講一下跟上面類似但不完全一樣的觀點。
啟發式合併是小點合併到大點,而我們希望一次合併並計算答案的複雜度跟小點有關,才會得到好的複雜度。因為只會越合併越差,那麼我們乾脆在合併的時候先不要計算答案,而是先把合併的順序記下來,一直到一個點要當小點的時候再一次用二分搜計算答案。仔細想想會發現這樣跟輕重鍊剖分要做的事情差不多,下面的 code 是用這樣的想法生出來的,希望複雜度是對的(還沒測過)
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe std::cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename ...T> void qqbx(const char *s, T ...args) {
int cnt = sizeof...(T);
((cerr << "\e[1;32m(" << s << ") = ("), ..., (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T> void danb(const char *s, T L, T R) {
cerr << "\e[1;32m[ " << s << " ] = [ ";
for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif // local
#define all(v) begin(v),end(v)
using namespace std;
const int maxn = 100025, maxm = 500025;
struct Edge {
char c;
int a, b;
Edge () = default;
Edge (char c, int a, int b) : c(c), a(a), b(b) {}
};
namespace impl {
vector<int> uG[maxn]; // undirected
vector<int> dG[maxn]; // directed
bool vis[maxn];
int ccid[maxn];
void uDfs(int i) {
vis[i] = true;
for (int j: uG[i]) {
if (!vis[j]) {
ccid[j] = ccid[i];
uDfs(j);
}
}
}
}
bool noCycle(const vector<Edge> &es) {
using namespace impl;
for (auto [c, a, b]: es)
for (int x: {a, b})
uG[x].clear(), vis[x] = false;
for (auto [c, a, b]: es)
if (c == '=')
uG[a].push_back(b), uG[b].push_back(a);
int ccid_counter = 0;
for (auto [c, a, b]: es)
for (int x: {a, b})
if (!vis[x]) {
ccid[x] = ccid_counter++;
uDfs(x);
}
vector<int> indeg(ccid_counter);
for (int i = 0; i < ccid_counter; i++)
dG[i].clear();
debug("HERE");
for (auto [c, a, b]: es)
if (c == '>') {
a = ccid[a];
b = ccid[b];
if (a == b)
return false;
dG[a].push_back(b), ++indeg[b];
}
queue<int> que;
for (int i = 0; i < ccid_counter; i++)
if (indeg[i] == 0)
que.push(i);
while (!que.empty()) {
int i = que.front(); que.pop();
for (int j: dG[i])
if (--indeg[j] == 0)
que.push(j);
}
for (int i = 0; i < ccid_counter; i++)
if (indeg[i] != 0)
return false;
return true;
}
namespace dsu {
int pa[maxm], sz[maxm];
vector<Edge> edges[maxm];
struct Op {
int qid;
Edge e;
Op (int qid) : qid(qid), e() {}
Op (Edge e) : qid(-1), e(e) {}
};
vector<Op> ops[maxm];
int ans[maxm];
void init(int n) {
for (int i = 0; i < n; i++)
pa[i] = i, sz[i] = 1;
}
int anc(int x) { return x==pa[x] ? x : pa[x]=anc(pa[x]); }
void doOps(int x) {
debug("doOps", x, ops[x].size());
const auto ok = [x](int p) -> bool {
vector<Edge> es = edges[x];
for (int i = 0; i < p; i++)
if (ops[x][i].qid == -1)
es.emplace_back(ops[x][i].e);
return noCycle(es);
};
int p = 0;
for (int s = 1<<20; s; s >>= 1)
if (p + s <= ops[x].size() && ok(p + s))
p += s;
for (int i = 0; i < p; i++)
if (ops[x][i].qid != -1)
ans[ops[x][i].qid] = true;
for (auto [qid, e]: ops[x])
if (qid == -1)
edges[x].emplace_back(e);
}
void join(int a, int b, int qid) {
a = anc(a), b = anc(b);
if (a == b) {
ops[a].emplace_back(qid);
return;
}
if (sz[a] < sz[b])
swap(a, b);
doOps(b);
sz[a] += sz[b];
pa[b] = a;
for (const Edge &e: edges[b])
ops[a].emplace_back(e);
ops[a].emplace_back(qid);
edges[b].clear();
ops[b].clear();
}
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
int n, m;
cin >> n >> m;
dsu::init(m);
for (int i = 0; i < m; i++) {
int a, b;
char c;
cin >> a >> c >> b;
--a, --b;
if (c == '<') {
c = '>';
swap(a, b);
}
dsu::edges[i].emplace_back(c, a, b);
}
for (int i = 1; i < m; i++) {
int a, b;
cin >> a >> b;
--a, --b;
dsu::join(a, b, i);
}
dsu::doOps(dsu::anc(0));
for (int i = 1; i < m; i++)
cout << (dsu::ans[i] ? "Yes\n" : "No\n");
}
``