非常好的模擬賽,使我的大腦旋轉。
終於不墊底了。
T1
有一張 \(n\) 個點的無向圖,初始沒有邊。
給定 \(m\) 次操作,每次操作給出集合 \(S\) 和 \(T\) ,並將滿足以下條件的邊 \((x,y)\) 存在狀態取反:
- \(1\le x<y\le n\)
- \(x\in S,y\in T\) 或 \(x\in T,y\in S\)
求 \(m\) 次操作後圖中的邊數。
\(n \le 10000,m\le 64\)
出題人給了個什麼 __builtin_popcount
的提示,感覺沒用,好像還誤導我了。不知道為啥直覺上覺得 bool
開一億會爆,發現空間很小就直接切了。
做法就是對於每個點維護周圍的直接連邊,bitset
模擬即可。
複雜度 \(O(\frac{nm}{w})\)。
#include <bits/stdc++.h>
using namespace std;
using ubt = long long;
#define vec vector
#define eb emplace_back
#define bg begin
#define mkp make_pair
#define fi first
#define se second
const int inf = 1e9;
const int maxN = 1e4 + 3;
int n, m;
bitset<maxN> b[maxN], ib[maxN];
vec<int> S, T, U;
bitset<maxN> s, t, u;
int main() {
ifstream cin("a.in");
ofstream cout("a.out");
cin >> n >> m;
for (int p = 1; p <= m; p++) {
S.clear(), T.clear(), U.clear();
s.reset(), t.reset(), u.reset();
for (int i = 0; i < n; i++) {
char c;
cin >> c;
if (c == '0') continue;
if (c == '1') S.eb(i), s.set(i);
if (c == '2') T.eb(i), t.set(i);
if (c == '3') U.eb(i), u.set(i);
}
for (int i : S)
b[i] ^= t ^ u;
for (int i : T)
b[i] ^= s ^ u;
for (int i : U)
b[i] ^= u ^ s ^ t;
// cerr << "T: " << t << '\n';
// cerr << "S: " << s << '\n';
// for (int i = 0; i < n; i++)
// cerr << i + 1 << ": " << b[i] << '\n';
}
int ans = 0;
for (int i = 0; i < n; i++)
b[i].reset(i), ans += b[i].count();
cout << ans / 2 << '\n';
}
貌似 DrRatio 用到提示了。
T2
昨天剛做皇后遊戲,今天直接考貪心了,感覺很厲害。賽時過了。
給定長為 \(n\) 的序列 \(a_1,a_2,\dots,a_n\) 和 \(m\) 次操作,每次操作有引數 \((t,x,y)\):
- \(t=1\) 表示修改 \(a_x^′=y\)
- \(t=2\) 表示修改 \(a_x^′=a_x+y\)
- \(t=3\) 表示修改 \(a_x^′=a_x\times y\)
從中選擇至多 \(k\) 個不同的操作並以任意順序執行,最大化最終的 \(\prod _{i=1}^n a_i\),答案對 \(1e9+7\) 取模。
發現所有操作都應該是賦值在前,加法隨後,乘法最後。賦值只有最大的有用,可以把賦值看成加上 \(y-a_i\),因為加法具有交換律,所以正確。
現在只有加法和乘法了。因為求的是 \(\prod\limits_i (a_i+\sum\limits_j x_j)\times \prod\limits_j y_j\),\(x\) 為第 \(i\) 個數的加法操作,\(y\) 是乘法操作。所以所有的乘法操作都是在外面的,乘法具有交換律,所以同一個數、不同數乘法和乘法之間的優先順序都是大的優先。就可以把乘法拿出來單獨考慮了。
對於同一個數的加法操作顯然是大的先。
問題現在在於不同數的加法操作優先順序,還有加法和乘法操作之間的優先順序。
因為你剛做完皇后遊戲,所以考慮類似臨項交換的方法。
\(i\) 的一個加法,比 \(j\) 的優:
\(i\) 的一個加法,比乘法優:
把所有加法操作扔到優先佇列裡,每次看最優的加法操作比不比最劣的乘法操作優,貪心的選就行了。
PS:被 hack 了,不過不是大問題,實現不太精細,有個地方爆 long long
了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
using ubt = long long;
#define vec vector
#define eb emplace_back
#define bg begin
#define mkp make_pair
#define fi first
#define se second
const int inf = 1e9;
const int maxN = 1e5 + 7, mod = 1e9 + 7;
int ksm(int a, int b = mod - 2) {
a %= mod;
int r = 1;
while (b) {
(b & 1) && (r = r * a % mod);
a = a * a % mod;
b >>= 1;
}
return r;
}
int n, m;
struct node {
int v;
vec<int> add;
int co;
friend bool operator < (const node &A, const node &B) {
auto a = A.add.back(), b = B.add.back();
return 1. * a * B.v < 1. * b * A.v;
}
} a[maxN];
int mul[maxN], kp[maxN], ml;
priority_queue<node> Q;
signed main() {
ifstream cin("b.in");
ofstream cout("b.out");
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i].v;
for (int i = 1; i <= m; i++) {
int t, x, y;
cin >> t >> x >> y;
if (t == 1)
if (y > a[x].co)
a[x].co = y;
if (t == 2)
a[x].add.eb(y);
if (t == 3)
mul[++ml] = y;
}
sort(mul + 1, mul + ml + 1, greater<int> ());
mul[0] = 1;
for (int i = 0; i <= ml; i++) kp[i] = mul[i];
for (int i = 1; i <= ml; i++)
mul[i] = mul[i - 1] * mul[i] % mod;
for (int i = 1; i <= n; i++) {
a[i].add.eb(-1);
if (a[i].co > a[i].v)
a[i].add.eb(a[i].co - a[i].v);
sort(a[i].add.bg(), a[i].add.end());
}
for (int i = 1; i <= n; i++)
Q.emplace(a[i]);
int cnt = 0;
int ans = 1;
auto calc = [&](int p, int tot) {
int num = tot - p;
if (num > ml) num = ml;
if (num < 0) num = 0;
return mul[num];
};
for (int i = 1; i <= n; i++) (ans *= a[i].v) %= mod;
for (int k = 0; k <= m; k++) {
while (cnt < k) {
auto t = Q.top();
auto ad = t.add.back();
if (ad == -1) break;
if (cnt + 1 + ml <= k || t.v * (kp[k - cnt] - 1) < ad) {
Q.pop();
cnt++;
ans = ans * ksm(t.v) % mod * (t.v + ad) % mod;
t.v += ad;
t.add.pop_back();
Q.emplace(t);
} else break;
}
auto res = ans * calc(cnt, k) % mod;
cout << res << ' ';
}
cout << '\n';
}
T4
給定長為 \(n\) 的字串 \(s\)(下標從 \(1\) 開始),其僅包含前 \(k\) 種小寫字母,共 \(m\) 種操作,為以下兩種之一:
\(∀i\in [l,r]\) 修改 \(s_i\) 為其後 \(c\) 個字母(這裡認為第 \(k\) 個小寫字母的後一個字母為 \(a\))
對於前 \(k\) 種小寫字母的一個排列 \(t\),在 \(s[l,r]\) 中插入若干字元使得其為若干個 \(t\) 拼接的結果
求最小的 \(\frac{∣s^′∣}{k}\)(\(s^′\) 為插入字元後的字串),注意並不真的在 \(s\) 中插入。
賽後看到線段樹就瞬間明白了。
試了一下各種引用,拿了最優解。
感覺挺智慧。將每個字元按 \(t\) 中位置編號,即求其極長遞增段的個數,也即相鄰兩個字元逆序的個數。
#include <bits/stdc++.h>
using namespace std;
#define vec vector
using Ar = array<array<int, 10>, 10>;
const int maxN = 2e5 + 7;
int n, m, k;
string s;
#define id(x) \
((x) >= k ? (x) - k : (x))
int tg[maxN * 2];
struct dat {
int l, r;
Ar f;
friend dat operator + (const dat &A, const dat &B) {
dat res;
res.l = A.l, res.r = B.r;
for (int i = 0; i < k; i++)
for (int j = 0; j < k; j++)
res.f[i][j] = A.f[i][j] + B.f[i][j];
res.f[A.r][B.l]++;
return res;
}
void operator += (const int &T) {
Ar tmp;
for (int i = 0; i < k; i++)
for (int j = 0; j < k; j++)
tmp[id(i + T)][id(j + T)] = f[i][j];
f = tmp;
l = id(l + T), r = id(r + T);
}
} t[maxN * 2];
#define ls (mid << 1)
#define rs (mid << 1 | 1)
void build(int l, int r, int p) {
if (l == r) {
t[p].l = t[p].r = s[l] - 'a';
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls), build(mid + 1, r, rs);
t[p] = t[ls] + t[rs];
}
inline void make(const int &p, int &lz) {
t[p] += lz;
tg[p] = id(tg[p] + lz);
}
inline void down(int &p, int &mid) {
if (!tg[p]) return;
make(ls, tg[p]);
make(rs, tg[p]);
tg[p] = 0;
}
void change(int &L, int &R, int &v, int p, int l, int r) {
if (L <= l && r <= R) return make(p, v);
int mid = (l + r) >> 1;
down(p, mid);
if (L <= mid)
change(L, R, v, ls, l, mid);
if (R > mid)
change(L, R, v, rs, mid + 1, r);
t[p] = t[ls] + t[rs];
}
dat ask(int &L, int &R, int p, int l, int r) {
if (L <= l && r <= R) return t[p];
int mid = (l + r) >> 1;
down(p, mid);
if (R <= mid)
return ask(L, R, ls, l, mid);
if (L > mid)
return ask(L, R, rs, mid + 1, r);
return ask(L, R, ls, l, mid) + ask(L, R, rs, mid + 1, r);
}
int main() {
ifstream cin("d.in");
ofstream cout("d.out");
cin >> n >> m >> k;
cin >> s;
s = '!' + s;
build(1, n, 1);
while (m--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 1) {
int c;
cin >> c;
change(l, r, c, 1, 1, n);
}
if (op == 2) {
string g;
cin >> g;
auto &&res = ask(l, r, 1, 1, n);
int ans = 1;
for (int i = 0; i < k; i++)
for (int j = i, x = g[i] - 'a'; j < k; j++) {
int y = g[j] - 'a';
ans += res.f[y][x];
}
cout << ans << '\n';
}
}
}