[赛记] 冲刺CSP联训模拟2
三道计数 + 一道数据结构也是没谁了
这场可是好好锻炼了我的写暴搜能力。。。
挤压 20pts
暴搜20pts;
把最后的答案进行二进制拆解,即 $ ans = 2^i + 2^j + 2^k + ... $,那么答案的平方为 $ \sum_{i = 0}^{30} \sum_{j = 0}^{30} s_i s_j 2^{i + j} $,其中 $ s_i, s_j $ 代表答案二进制拆解后这一位是 $ 1 $ 还是 $ 0 $;
那么我们发现,答案的平方只和答案二进制拆解后的任意两位有关,所以我们直接设 $ f_{k, i, j, 0/1, 0/1} $ 表示考虑到第 $ k $ 个数字,第 $ i $ 位是 $ 0/1 $,第 $ j $ 位是 $ 0/1 $ 的概率即可;
这其实是个概率 $ DP $,从第一步开始其实比较难想;
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const long long mod = 1e9 + 7;
long long n;
long long a[500005], q[500005];
long long ksm(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;
}
long long f[35][35][2][2], now[2][2];
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
long long kk = ksm(1000000000, mod - 2);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
cin >> q[i];
q[i] = q[i] * kk % mod;
}
for (int i = 0; i <= 30; i++) {
for (int j = 0; j <= 30; j++) {
f[i][j][0][0] = 1;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= 30; j++) {
for (int k = 0; k <= 30; k++) {
for (int l = 0; l <= 1; l++) {
for (int r = 0; r <= 1; r++) {
now[l][r] = f[j][k][l][r];
}
}
bool tj = false;
bool tk = false;
if ((a[i] >> j) & 1) tj = true;
if ((a[i] >> k) & 1) tk = true;
for (int l = 0; l <= 1; l++) {
for (int r = 0; r <= 1; r++) {
f[j][k][l][r] = (now[l][r] * (1 - q[i] + mod) % mod + now[l ^ tj][r ^ tk] * q[i] % mod) % mod;
}
}
}
}
}
long long ans = 0;
for (int i = 0; i <= 30; i++) {
for (int j = 0; j <= 30; j++) {
ans = (ans + f[i][j][1][1] * ksm(2, i + j) % mod) % mod;
}
}
cout << ans;
return 0;
}
工地难题 30pts
暴搜30pts
考虑正解,是个容斥,题解详见[算法] 容斥的例题;
星空遗迹 20pts
$ \Theta(n^3) $ 暴力20pts;
考虑正解,发现可以单调栈维护,满足下面的能够赢上面的,答案就是栈大小最小时对应的元素,于是可以线段树区间修改区间查最小;
时间复杂度:$ \Theta(n \log n) $;
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n, q;
char s[500005];
char win(char x, char y) {
if (x == y) return x;
if ((x == 'R' && y == 'S') || (x == 'S' && y == 'R')) return 'R';
if ((x == 'S' && y == 'P') || (x == 'P' && y == 'S')) return 'S';
if ((x == 'P' && y == 'R') || (x == 'R' && y == 'P')) return 'P';
}
int f[500005];
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, lz;
pair<int, int> mi;
}tr[3000005];
inline void push_up(int id) {
tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
}
inline void push_down(int id) {
if (tr[id].lz) {
tr[ls(id)].lz += tr[id].lz;
tr[rs(id)].lz += tr[id].lz;
tr[ls(id)].mi.first += tr[id].lz;
tr[rs(id)].mi.first += tr[id].lz;
tr[id].lz = 0;
}
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
tr[id].mi = {f[l], l};
return;
}
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
push_up(id);
}
void add(int id, int l, int r, int d) {
if (tr[id].l >= l && tr[id].r <= r) {
tr[id].mi.first += d;
tr[id].lz += d;
return;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (l <= mid) add(ls(id), l, r, d);
if (r > mid) add(rs(id), l, r, d);
push_up(id);
}
pair<int, int> ask(int id, int l, int r) {
if (tr[id].l >= l && tr[id].r <= r) {
return tr[id].mi;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (r <= mid) return ask(ls(id), l, r);
else if (l > mid) return ask(rs(id), l, r);
else return min(ask(ls(id), l, mid), ask(rs(id), mid + 1, r));
}
}
using namespace SEG;
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
f[1] = 1;
for (int i = 2; i <= n; i++) {
if (s[i] == s[i - 1]) {
f[i] = f[i - 1];
continue;
}
if (win(s[i], s[i - 1]) == s[i]) {
f[i] = f[i - 1] - 1;
continue;
}
if (win(s[i], s[i - 1]) == s[i - 1]) {
f[i] = f[i - 1] + 1;
continue;
}
}
bt(1, 1, n);
int ss, l, r;
char y;
for (int i = 1; i <= q; i++) {
cin >> ss >> l;
if (ss == 1) {
cin >> y;
if (y == s[l]) continue;
if (l > 1) {
int ls = 0;
if (s[l - 1] == s[l]) ls = 0;
else if (win(s[l - 1], s[l]) == s[l - 1]) ls = 1;
else if (win(s[l - 1], s[l]) == s[l]) ls = -1;
int now = 0;
if (s[l - 1] == y) now = 0;
else if (win(s[l - 1], y) == s[l - 1]) now = 1;
else if (win(s[l - 1], y) == y) now = -1;
add(1, l, n, now - ls);
}
if (l < n) {
int ls = 0;
if (s[l + 1] == s[l]) ls = 0;
else if (win(s[l], s[l + 1]) == s[l]) ls = 1;
else if (win(s[l], s[l + 1]) == s[l + 1]) ls = -1;
int now = 0;
if (y == s[l + 1]) now = 0;
else if (win(s[l + 1], y) == y) now = 1;
else if (win(s[l + 1], y) == s[l + 1]) now = -1;
add(1, l + 1, n, now - ls);
}
s[l] = y;
}
if (ss == 2) {
cin >> r;
cout << s[ask(1, l, r).second] << '\n';
}
}
return 0;
}