鏡的綺想 (mirror) 100pts
考慮 $ \Theta(nm) $ 的做法,發現我們可以對於每一對實點和虛點求它們的“鏡面”,然後得到 $ \Theta(nm) $ 個“鏡面”,發現這些直線只可能是形如 $ y = 0.5x, x \in Z $ 的直線,所以我們直接乘 $ 2 $,然後開個桶統計一下即可;
時間複雜度:$ \Theta(nm) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n, m;
bool vis[3000005];
vector<int> shi[3000005], xu[3000005], v;
int sum[5000005];
int main() {
freopen("mirror.in", "r", stdin);
freopen("mirror.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int x, y;
for (int i = 1; i <= n; i++) {
cin >> x >> y;
x += 1e6;
y += 1e6;
shi[x].push_back(y);
if (!vis[x]) {
vis[x] = true;
v.push_back(x);
}
}
for (int i = 1; i <= m; i++) {
cin >> x >> y;
x += 1e6;
y += 1e6;
xu[x].push_back(y);
if (!vis[x]) {
vis[x] = true;
v.push_back(x);
}
}
int ans = 0;
for (int i = 0; i < v.size(); i++) {
for (int j = 0; j < shi[v[i]].size(); j++) {
for (int k = 0; k < xu[v[i]].size(); k++) {
sum[(shi[v[i]][j] + xu[v[i]][k])]++;
ans = max(ans, sum[(shi[v[i]][j] + xu[v[i]][k])]);
}
}
}
cout << ans;
return 0;
}
萬物有靈 (animism) -pts
賽時不會獨立集,所以沒寫;
考慮獨立集就是一個圖中的點集,其中任意兩點沒有邊相連;
那麼對於這棵樹,我們發現,它的最大獨立集是從最後一層開始,每隔一層選一層,最後選到根,因為每往下走,它的節點數只增不減;
這樣樸素實現是 $ \Theta(n) $ 的,考慮最佳化;
發現它有一個迴圈節,每 $ 2k $ 次就會回來,所以我們先暴力把週期翻倍,可以考慮計算迴圈節的貢獻 + 剩餘貢獻,後者直接暴力,前者可以考慮DP,設 $ f_{i} $ 表示前 $ i $ 個迴圈節的貢獻和,那麼有轉移 $ f_{i} = \prod a \times f_{i - 1} + f_1 $,就是把前面的左移一位,最後再加上 $ f_1 $;
這樣就可以矩陣加速,所以時間複雜度:$ \Theta(\log n + k) $;
細節有些多;
點選檢視程式碼
#include <iostream>
#include <cstdio>
using namespace std;
long long nn, k, mod;
long long a[2000005];
struct Mat{
long long a[5][5];
int n, m;
inline void clear() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = 0;
}
}
}
inline void one() {
for (int i = 1; i <= n; i++) a[i][i] = 1;
}
inline void rsize(int x, int y) {
n = x;
m = y;
}
inline Mat operator *(const Mat &A) const {
Mat ans;
ans.rsize(n, A.m);
ans.clear();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= A.m; j++) {
for (int k = 1; k <= m; k++) {
ans.a[i][j] = (ans.a[i][j] + a[i][k] * A.a[k][j] % mod) % mod;
}
}
}
return ans;
}
};
Mat ksm(Mat A, long long b) {
Mat ans;
ans.rsize(2, 2);
ans.clear();
ans.one();
while(b) {
if (b & 1) ans = ans * A;
A = A * A;
b >>= 1;
}
return ans;
}
long long qpow(long long a, long long b) {
long long ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int main() {
freopen("animism.in", "r", stdin);
freopen("animism.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> nn >> k >> mod;
for (int i = 1; i <= k; i++) {
cin >> a[i];
a[i + k] = a[i];
}
k *= 2;
if (nn <= k) {
long long ans = 1;
long long now = 1;
if (!(nn & 1)) {
ans = 1;
for (int i = 1; i <= nn; i++) {
now = now * a[i] % mod;
if (!(i & 1)) ans = (ans + now) % mod;
}
} else {
ans = 0;
for (int i = 1; i <= nn; i++) {
now = now * a[i] % mod;
if (i & 1) ans = (ans + now) % mod;
}
}
cout << ans;
return 0;
}
long long w = 1, f = 0;
for (int i = 1; i <= k; i++) {
w = w * a[i] % mod;
if (!(nn & 1)) {
if (!(i & 1)) f = (f + w) % mod;
} else {
if (i & 1) f = (f + w) % mod;
}
}
Mat B;
B.rsize(2, 2);
B.a[1][1] = w; B.a[1][2] = 1; B.a[2][1] = 0; B.a[2][2] = 1;
long long res = (nn + (nn & 1)) / k;
B = ksm(B, res - 1);
long long now = qpow(w, res);
long long ans = (B.a[1][1] * f % mod + B.a[1][2] * f % mod) % mod;
if (!(nn & 1)) ans = (ans + 1) % mod;
long long ret = nn % k;
for (int i = 1; i <= ret; i++) {
now = now * a[i] % mod;
if (ret & 1) {
if (i & 1) ans = (ans + now) % mod;
} else {
if (!(i & 1)) ans = (ans + now) % mod;
}
}
cout << ans;
return 0;
}
白石溪 (creek) 30pts
暴力DP還掛了15pts。。。
DP不好最佳化,考慮貪心;
我們欽定一開始全是藍色,那麼每次把一個藍色變成紅色,貢獻為 a[i] - b[i] + (i - 1) * d + (n - i) * c - now * (d + c)
,其中 $ now $ 是現在有的(不算他自己)的紅色個數,我們發現減號後面的一項在這個狀態下是相同的,所以直接按 a[i] - b[i] + (i - 1) * d + (n - i) * c
排序選最大即可;
時間複雜度: $ \Theta(n \log n) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
long long n, c, d;
long long f[2][1000005];
long long a[1000005], b[1000005];
priority_queue<long long> q;
int main() {
freopen("creek.in", "r", stdin);
freopen("creek.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> c >> d;
long long ans = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
ans += b[i];
q.push(a[i] - b[i] + (i - 1) * d + (n - i) * c);
}
long long now = 0;
long long sum = ans;
while(!q.empty()) {
long long t = q.top();
q.pop();
sum += t - now * (d + c);
ans = max(ans, sum);
now++;
}
cout << ans;
return 0;
}
上山崗 (uphill) 15pts
我們首先讓人從小到大選山,如果他能選就選最後面的,可以發現,這樣是可以得到一個最大解的,這個直接線段樹二分即可解決;
考慮將大數前移,不難發現,如果這個數在前一步操作中匹配上了,那麼我們只能將其移到沒有匹配的山去匹配(不會出現和小數互換的情況,因為如果能換,這個位置就是較小的那個數了)。如果沒有匹配,考慮他能不能將一個小數換掉,或者去一個空位上(前面大的數留下來的),兩者取最大即可;
開兩棵線段樹,然後線段樹二分即可解決這個問題,時間複雜度: $ \Theta(n \log n) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n;
int a[500005], b[500005], pos[500005], ans[500005];
bool vis[500005];
vector<int> v;
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, mi;
}tr[3000005];
inline void push_up(int id) {
tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
tr[id].mi = a[l];
return;
}
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
push_up(id);
}
void bt1(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
if (!vis[l]) tr[id].mi = a[l];
else tr[id].mi = 2e9;
return;
}
int mid = (l + r) >> 1;
bt1(ls(id), l, mid);
bt1(rs(id), mid + 1, r);
push_up(id);
}
int ask(int id, int d) {
if (tr[id].l == tr[id].r) return tr[id].l;
if (tr[rs(id)].mi < d) return ask(rs(id), d);
else return ask(ls(id), d);
}
int askk(int id, int d) {
if (tr[id].l == tr[id].r) return tr[id].l;
if (tr[ls(id)].mi < d) return askk(ls(id), d);
else return askk(rs(id), d);
}
void del(int id, int pos) {
if (tr[id].l == tr[id].r) {
tr[id].mi = 2e9;
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) del(ls(id), pos);
else del(rs(id), pos);
push_up(id);
}
void add(int id, int pos) {
if (tr[id].l == tr[id].r) {
tr[id].mi = a[tr[id].l];
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) add(ls(id), pos);
else add(rs(id), pos);
push_up(id);
}
}
namespace seg{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, mi;
}tr[3000005];
inline void push_up(int id) {
tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
if (vis[l]) tr[id].mi = a[l];
else tr[id].mi = 2e9;
return;
}
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
push_up(id);
}
void del(int id, int pos) {
if (tr[id].l == tr[id].r) {
tr[id].mi = 2e9;
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) del(ls(id), pos);
else del(rs(id), pos);
push_up(id);
}
int ask(int id, int d) {
if (tr[id].l == tr[id].r) return tr[id].l;
if (tr[ls(id)].mi < d) return ask(ls(id), d);
else return ask(rs(id), d);
}
int askk(int id, int pos) {
if (tr[id].l == tr[id].r) return tr[id].mi;
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) return askk(ls(id), pos);
else return askk(rs(id), pos);
push_up(id);
}
}
int main() {
freopen("uphill.in", "r", stdin);
freopen("uphill.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
SEG::bt(1, 1, n);
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++) {
if (SEG::tr[1].mi >= b[i]) continue;
pos[i] = SEG::ask(1, b[i]);
vis[pos[i]] = true;
SEG::del(1, pos[i]);
}
seg::bt(1, 1, n);
SEG::bt1(1, 1, n);
for (int i = n; i >= 1; i--) {
int p = seg::askk(1, pos[i]);
if (p == 2e9) {
if (seg::tr[1].mi >= b[i]) {
v.push_back(b[i]);
continue;
}
int now = seg::ask(1, b[i]);
int ma = SEG::askk(1, 1e9);
if (now < ma) {
ans[now] = b[i];
seg::del(1, now);
} else {
ans[ma] = b[i];
SEG::del(1, ma);
}
} else {
SEG::add(1, pos[i]);
seg::del(1, pos[i]);
int now = SEG::askk(1, b[i]);
ans[now] = b[i];
SEG::del(1, now);
}
}
int now = 0;
for (int i = 1; i <= n; i++) {
if (!ans[i]) {
cout << v[now] << ' ';
now++;
} else cout << ans[i] << ' ';
}
return 0;
}