A. Maximum Element In A Stack
按照題目模擬就好,棧內的最大值可以維護單調棧解決。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui32 = unsigned int;
ui32 SA, SB, SC;
ui32 rng61(){
SA ^= SA << 16;
SA ^= SA >> 5;
SA ^= SA << 1;
ui32 t = SA;
SA = SB;
SB = SC;
SC ^= t ^ SA;
return SC;
}
void solve() {
int n, p, q, m;
cin >> n >> p >> q >> m >> SA >> SB >> SC;
stack<ui32> s;
i64 res = 0;
for(ui32 i = 1; i <= n ; i ++) {
if(rng61() % (p + q) < p) {
ui32 tmp = (rng61() % m + 1);
if(s.empty()) s.push(tmp);
else s.push(max(s.top(), tmp));
} else if(not s.empty()) s.pop();
if(not s.empty()) res ^= (i64)i * (i64)s.top();
}
cout << res << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
cin >> TC;
for(int i = 1; i <= TC; i ++)
cout << "Case #" << i << ": ", solve();
return 0;
}
B. Rolling The Polygon
偏模板的題目,透過三個點算出夾角,然後根據半徑算一下弧長就好了。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9;
using db = long double;
struct Point {
db x, y;
Point(db x = 0, db y = 0) : x(x), y(y) {};
};
using Vec = Point;
const db pi = acosl(-1);
Vec operator+(Vec u, Vec v) { return Vec(u.x + v.x, u.y + v.y); }
Vec operator-(Vec u, Vec v) { return Vec(u.x - v.x, u.y - v.y); }
db operator*(Vec u, Vec v) { return u.x * v.x + u.y * v.y; }
db len(Vec v) { return sqrt(v.x * v.x + v.y * v.y); }
db cos_t(Vec u, Vec v) { return u * v / len(u) / len(v); }
using Points = vector<Point>;
ostream &operator<<(ostream &os, Point x) {
return os << "(" << x.x << "," << x.y << ")";
}
void solve() {
int n;
cin >> n;
Points ps(n);
for (auto &[x, y]: ps) cin >> x >> y;
Point po;
cin >> po.x >> po.y;
db res = 0;
for (int i = 0, lst = n - 1, nxt = 1; i < n; i++, lst = (lst + 1) % n, nxt = (nxt + 1) % n) {
Vec v1 = ps[nxt] - ps[i], v2 = ps[lst] - ps[i];
db theta = acosl(cos_t(v1, v2));
res += (pi - theta) * len(po - ps[i]);
}
cout << fixed << setprecision(3) << res << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1;
cin >> T;
for (int i = 1; i <= T; i++)
cout << "Case #" << i << ": ", solve();
}
C. Caesar Cipher
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, m;
cin >> n >> m;
string a, b, c;
cin >> a >> b >> c;
int d = (a.front() - b.front() + 26) % 26;
for (auto i: c)
cout << char((i - 'A' + d) % 26 + 'A');
cout << "\n";
return ;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1;
cin >> T;
for( int i = 1 ; i <= T ; i ++ )
cout << "Case #" << i << ": ", solve();
return 0;
}
D. Take Your Seat
打表找找規律就好了
#include <bits/stdc++.h>
using namespace std;
#define ll long long
using vi = vector<int>;
void solve() {
int n, m;
cin >> n >> m;
long double x = 1, y = 0;
for (int i = 1; i <= m; i++)
x += 1, y += 2;
cout << fixed << setprecision(6) << (n == 1 ? 1.0 : 0.5) << " " << x / y << "\n";
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
for (int i = 1; i <= T; i++) {
cout << "Case #" << i << ": ";
solve();
}
return 0;
}
F. Moving On
離線做,詢問按照\(w\)進行排序。然後每次加入一個點之後,把進行一次鬆弛操作。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9;
void solve() {
int n, q;
cin >> n >> q;
vector<pii> a;
for (int i = 1, x; i <= n; i++)
cin >> x, a.emplace_back(x, i);
sort(a.begin(), a.end());
vector g(n + 1, vi(n + 1));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) cin >> g[i][j];
vector<array<int, 4>> op(q);
for (int id = 0; auto &[w, u, v, idx]: op)
cin >> u >> v >> w, idx = id++;
sort(op.begin(), op.end());
vi res(q);
for (int t = 0; const auto &[w, u, v, idx]: op) {
while (t < n and a[t].first <= w) {
int x = a[t].second;
for (int i = 1; i <= n; i++)
for (int j = 1; j < i; j++)
g[i][j] = g[j][i] = min(g[i][j], g[i][x] + g[x][j]);
t++;
}
res[idx] = g[u][v];
}
for (auto &i: res) cout << i << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1;
cin >> T;
for (int i = 1; i <= T; i++)
cout << "Case #" << i << ":\n", solve();
return 0;
}
G. Factories
這本身是一個無根樹,所以先找一個不是葉子的點做根。
\(dp[i][j]\)表示\(i\)的子樹中選擇\(k\)個葉子點的最小代價,\(u\)是根節點,\(v\)是子節點,則轉移如下
\[dp[u][i] = dp[u][i-1] + dp[v][j] + w * (k - j) * j
\]
其中\(w*(k-j)*j\)表示從\(v\)到\(u\)的邊產生的貢獻。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll inf = 1e18;
const int N = 1e5 + 6;
int n,k;
ll dp[N][110];
vector<pair<int,ll>> e[N];
void dfs(int u,int prv){
for (auto [v,w] : e[u]) if (v != prv){
dfs(v,u);
for (int i = k;i >= 0;i--){
for (int j = i;j >= 0;j--){
dp[u][i] = min(dp[u][i],dp[u][i - j] + dp[v][j] + w * (k - j) * (j));
}
}
}
}
void solve(){
cin >> n >> k;
for (int i = 1;i <= n;i++){
e[i].clear();
}
for (int i = 1;i <= n - 1;i++){
int u,v,w;cin >> u >> v >> w;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
int root = -1;
if (k == 1){
cout << 0 << endl;
return;
}
for (int i = 1;i <= n;i++){
if (e[i].size() != 1){
root = i;
break;
}
}
for (int i = 1;i <= n;i++){
for (int j = 0;j <= k;j++){
dp[i][j] = inf;
}
}
for (int i = 1;i <= n;i++){
dp[i][0] = 0;
}
for (int i = 1;i <= n;i++){
if (e[i].size() == 1){
dp[i][1] = 0;
}
}
if (root == -1){
int ans = 0;
for (int i = 1;i <= n;i++){
for (auto [v,w] : e[i]){
ans += w;
}
}
cout << ans / 2 << endl;
}else{
dfs(root,-1);
cout << dp[root][k] << endl;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;cin >> T;
for (int i = 1;i <= T;i++){
cout << "Case #" << i << ": ";
solve();
}
return 0;
}
H. Fight Against Monsters
貪心做,先計算出擊敗一個怪物需要的次數\(cnt\),然後按照\(\frac {stk} {cnt}\)從大到小逐個擊殺。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
void solve() {
int n;
cin >> n;
vector<tuple<double, int, int>> a;
int sum = 0;
for (int i = 1, hp, atk; i <= n; i++) {
cin >> hp >> atk, sum += atk;
int l = 1, r = hp, cnt = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (mid * (mid + 1) / 2 >= hp) cnt = mid, r = mid - 1;
else l = mid + 1;
}
a.emplace_back(double(atk) / (double) cnt, atk, cnt);
}
sort(a.begin(), a.end(), greater<>());
int res = 0;
for (auto &[x, atk, cnt]: a) {
res += cnt * sum;
sum -= atk;
}
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
cin >> TC;
for (int i = 1; i <= TC; i++)
cout << "Case #" << i << ": ", solve();
return 0;
}
K. Vertex Covers
我們可以把點分成兩部分,然後可以先計算左邊的部分,如果左邊有一個點不在集合中,則左邊所有與他相連的點都必須在集合內,否則不合法。
同理,在列舉右邊的集合。對於一個合法的集合,當右邊一個點不在集合中,則與這個點所有相連的左側的點必須在左側集合中,我們可以根據這個求出左邊集合的必選點,這左邊集合必須是必選點的超集。
我們可以用高維字首和,處理出左邊集合每個集合超集的和。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
#define int long long
using vi = vector<int>;
void solve(){
int n, m, p;
cin >> n >> m >> p;
vi val(n);
for(auto &i : val) cin >> i;
vector e(n ,vi(n));
for(int u, v; m; m --) {
cin >> u >> v, u --, v --;
e[u][v] = e[v][u] = 1;
}
int n1 = n / 2, n2 = n - n1;
vi f(1<<n1);
for(int i = 0; i < (1<<n1); i ++ ){
auto tmp = 1;
for(int j = 0; j < n1 and tmp; j ++){
if(i & (1<<j)) tmp = tmp * val[j] % p;
else {
for(int k = 0; k < n1 and tmp; k ++)
if(not (i & (1<<k)) and e[j][k] ) // k 也不在集合中,且 j,k 之間有邊
tmp = 0;
}
}
f[i] = tmp;
}
// SOS DP, 列舉超集
for(int j = 0; j < n1; j ++)
for(int i = 0; i < (1<<n1); i ++)
if(not(i &(1<<j))) f[i] = (f[i] + f[i ^ (1<<j)]) % p;
int res = 0;
for(int i = 0; i < (1<<n2); i ++){
int tmp = 1;
for( int j = 0; j < n2 and tmp; j ++){
if(i & (1<<j)) tmp = tmp * val[n1 + j] % p;
else {
for(int k = 0; k < n2 and tmp; k ++)
if(not (i & (1<<k)) and e[n1 + j][n1 + k])
tmp = 0;
}
}
if(tmp == 0) continue;
int need = 0;
for(int j = 0; j < n2; j ++){
if(i & (1<<j)) continue;
for(int k = 0; k < n1; k ++)
if(e[n1 + j][k]) // j 沒被選 且 j,k 之間存在邊,則 k 必選
need |= (1<<k);
}
res = (res + tmp * f[need] % p) % p;
}
cout << res << "\n";
return;
}
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
for(int i = 1; i <= T; i ++)
cout << "Case #" << i << ": ", solve();
return 0;
}