暴力操作(opt)30pts
這個錯解可反悔貪心30pts;
考慮正解,我們只需考慮前 $ \frac n2 + 1 $ 小的數即可;
考慮二分出一箇中位數 $ mid $,那麼我們要讓大於它的都用最小的代價變小;
考慮如何求這個最小的代價,因為 $ \lfloor \frac{\lfloor \frac ab \rfloor}{c} \rfloor = \lfloor \frac{ab}{c} \rfloor $,我們可以預處理出所有對於除以一個數 $ i $ 的最小代價,這個可以調和級數處理,即 $ c_{i \times j} = \min(c_{i \times j}, c_i + c_j) $,然後處理完以後維護一個字尾 $ \min $ 即可;
我們還要考慮除成 $ 0 $ 的情況,我們把這種情況的最小值存到 $ c_{n + 1} $ 中,那麼我們可以得到 $ c_{n + 1} = \min_{i = 1}^{n}(c_{n + 1}, c_i + c_{\lfloor \frac ni \rfloor + 1}) $,最後再維護一個字尾 $ \min $ 即可;
然後直接 check
就沒了;
時間複雜度:$ \Theta(n \log n) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
long long n, m, k;
long long a[500005], c[500005];
bool ck(int x) {
long long ans = 0;
for (int i = 1; i <= n / 2 + 1; i++) {
if (a[i] > x) ans += c[a[i] / (x + 1) + 1];
}
return (ans <= k);
}
int main() {
freopen("opt.in", "r", stdin);
freopen("opt.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> c[i];
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= m; i++) {
for (int j = 1; j * i <= m; j++) {
c[i * j] = min(c[i * j], c[i] + c[j]);
}
}
for (int i = m - 1; i >= 1; i--) c[i] = min(c[i], c[i + 1]);
c[m + 1] = 2e9;
for (int i = 2; i <= m; i++) {
c[m + 1] = min(c[m + 1], c[i] + c[m / i + 1]);
}
for (int i = m; i >= 1; i--) {
c[i] = min(c[i], c[i + 1]);
}
int l = 0;
int r = m;
int ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if (ck(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << ans;
return 0;
}
異或連通(xor)0pts
重點在於想到線段樹分治;
然後直接考慮線段樹分治(這個題直接 $ Trie $ 上分治就行),因為發現每條邊在所有詢問中出現的都是若干段不超過 $ \log k $ 段區間中的;
考慮首先建出 $ 01Trie $,那麼這些詢問從小到大在這個 $ 01Trie $ 上是連續的;
然後考慮一條邊對於那些詢問有貢獻,可以發現,如果二進位制位上這條邊的 $ c $ 為 $ 0 $,而 $ Trie $ 上為 $ 1 $ ,異或起來就是 $ 1 $ ,$ k $ 這一位為 $ 1 $ 時可能成立,為 $ 0 $ 不成立,所以依據這個我們就可以直接給子樹打上標記,然後直接 $ Trie $ 上分治即可;
時間複雜度:$ \Theta(n \log n \log V) $,其中 $ V $ 是值域;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <map>
using namespace std;
int n, m, q, k;
int x[500005], y[500005], c[500005], fa[500005], siz[500005], s[500005];
int find(int x) {
if (x == fa[x]) return fa[x];
else return find(fa[x]);
}
int tot, rt;
stack<pair<pair<int, int>, int> > sk;
long long ans;
map<int, long long> mp;
int p[35];
inline void merge(int x, int y) {
x = find(x);
y = find(y);
if (siz[x] < siz[y]) {
sk.push({{y, siz[y]}, x});
if (x == y) return;
ans -= (1ll * siz[x] * (siz[x] - 1) / 2);
ans -= (1ll * siz[y] * (siz[y] - 1) / 2);
siz[y] += siz[x];
ans += (1ll * siz[y] * (siz[y] - 1) / 2);
fa[x] = y;
} else {
sk.push({{x, siz[x]}, y});
if (x == y) return;
ans -= (1ll * siz[x] * (siz[x] - 1) / 2);
ans -= (1ll * siz[y] * (siz[y] - 1) / 2);
siz[x] += siz[y];
ans += (1ll * siz[x] * (siz[x] - 1) / 2);
fa[y] = x;
}
}
namespace Trie{
struct sss{
int ls, rs, id;
vector<int> v;
}tr[5000005];
void add(int now, int &id, int d) {
if (!id) id = ++tot;
if (!now) {
tr[id].id = d;
return;
}
if ((d >> (now - 1)) & 1) add(now - 1, tr[id].ls, d);
else add(now - 1, tr[id].rs, d);
}
void add_e(int now, int id, int x, int d) {
if (!id || !now) return;
int v = ((d >> (now - 1)) & 1);
if (p[now]) {
if (v) {
tr[tr[id].ls].v.push_back(x);
add_e(now - 1, tr[id].rs, x, d);
} else {
tr[tr[id].rs].v.push_back(x);
add_e(now - 1, tr[id].ls, x, d);
}
} else {
if (v) add_e(now - 1, tr[id].ls, x, d);
else add_e(now - 1, tr[id].rs, x, d);
}
}
void dfs(int now, int id) {
if (!id) return;
for (int i = 0; i < tr[id].v.size(); i++) {
int o = tr[id].v[i];
merge(x[o], y[o]);
}
if (!now) {
mp[tr[id].id] = ans;
for (int i = 0; i < tr[id].v.size(); i++) {
pair<pair<int, int>, int> t = sk.top();
sk.pop();
if (t.first.first == t.second) continue;
ans -= (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
siz[t.first.first] = t.first.second;
fa[t.second] = t.second;
ans += (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
ans += (1ll * siz[t.second] * (siz[t.second] - 1) / 2);
}
return;
}
dfs(now - 1, tr[id].ls);
dfs(now - 1, tr[id].rs);
for (int i = 0; i < tr[id].v.size(); i++) {
pair<pair<int, int>, int> t = sk.top();
sk.pop();
if (t.first.first == t.second) continue;
ans -= (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
siz[t.first.first] = t.first.second;
fa[t.second] = t.second;
ans += (1ll * siz[t.first.first] * (siz[t.first.first] - 1) / 2);
ans += (1ll * siz[t.second] * (siz[t.second] - 1) / 2);
}
}
}
using namespace Trie;
int main() {
freopen("xor.in", "r", stdin);
freopen("xor.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> q >> k;
for (int i = 1; i <= 32; i++) {
p[i] = ((k >> (i - 1)) & 1);
}
for (int i = 1; i <= m; i++) {
cin >> x[i] >> y[i] >> c[i];
}
for (int i = 1; i <= q; i++) {
cin >> s[i];
add(32, rt, s[i]);
}
for (int i = 1; i <= m; i++) {
add_e(32, rt, i, c[i]);
}
for (int i = 1; i <= n; i++) {
fa[i] = i;
siz[i] = 1;
}
dfs(32, 1);
for (int i = 1; i <= q; i++) {
cout << mp[s[i]] << '\n';
}
return 0;
}
民主投票(election)25pts
呵呵,想不到;
然後直接二分答案,二分的是每個點最多被投的票數(當票數一樣時),然後得到一個答案 $ res $;
考慮對於一個點 $ x $ ,如果 $ siz_x - 1 < res $,那麼不可以,如果 $ siz_x - 1 > res $,那麼可以,如果相等,考慮 $ res - 1 $ 對於這個點合不合法,我們就一直向上傳到根,如果此時根能接收到這個票,那就可行,否則不行;
時間複雜度:$ \Theta(Tn \log n) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
using namespace std;
int t;
int n;
int fa[5000005];
struct sss{
int t, ne;
}e[5000005];
int h[5000005], cnt;
void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
int siz[5000005];
void dfs(int x) {
siz[x] = 0;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
dfs(u);
siz[x] += siz[u] + 1;
}
}
int f[5000005];
int ans[5000005];
void afs(int x, int y) {
f[x] = 0;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
afs(u, y);
f[x] += f[u];
}
f[x] = max(0, f[x] - y) + 1;
}
bool ck(int x) {
afs(1, x);
return (f[1] == 1);
}
void cfs(int x, int y) {
if (siz[x] == y) {
ans[x] = 1;
return;
}
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (f[u] > 1) cfs(u, y);
}
}
int main() {
freopen("election.in", "r", stdin);
freopen("election.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while(t--) {
cin >> n;
for (int i = 2; i <= n; i++) {
cin >> fa[i];
add(fa[i], i);
}
dfs(1);
int l = 1;
int r = n;
int res = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if (ck(mid)) {
res = mid;
r = mid - 1;
} else l = mid + 1;
}
for (int i = 1; i <= n; i++) {
if (siz[i] > res) ans[i] = 1;
else ans[i] = 0;
}
afs(1, res - 1);
if (f[1] == 2) { //根自己一個 + 上傳的一個
cfs(1, res);
}
for (int i = 1; i <= n; i++) {
cout << ans[i];
}
cout << '\n';
for (int i = 1; i <= n; i++) h[i] = 0;
cnt = 0;
}
return 0;
}