[赛记] 多校A层冲刺NOIP2024模拟赛25
图 (a) 100pts
看到提示中有数一个数的二进制下 $ 1 $ 的个数,所以考虑怎么转化这个题,考虑开 $ n $ 个 bitset
,第 $ i $ 个 bitset
中如果第 $ j $ 位是 $ 1 $,代表存在边 $ (i, j) $,否则不存在,对于每次修改,直接将 $ S, T $ 两个集合对应的每个点互相异或一下即可;
然后最后统计一下,注意我们只考虑形如 $ (i, j), i < j $ 的边,这样可以避免重复;
时间复杂度:$ \Theta(\frac{n^2 m}{w}) $,正好可以过;
其实这题应该是跑不满,所以较轻松;
点击查看代码
#include <iostream>
#include <cstdio>
#include <bitset>
using namespace std;
int n, m;
bitset<10005> b[10005], s, t;
char a[10005];
long long ans;
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 >> m;
for (int i = 1; i <= m; i++) {
cin >> (a + 1);
s.reset();
t.reset();
for (int j = 1; j <= n; j++) {
if (a[j] == '1') t[j] = 1;
else if (a[j] == '2') s[j] = 1;
else if (a[j] == '3') {
t[j] = 1;
s[j] = 1;
}
}
for (int j = 1; j <= n; j++) {
if (a[j] == '1') b[j] ^= s;
else if (a[j] == '2') b[j] ^= t;
else if (a[j] == '3') {
b[j] ^= (s | t);
}
}
}
for (int i = 1; i <= n; i++) {
b[i] >>= (i + 1);
ans += 1ll * b[i].count();
}
cout << ans;
return 0;
}
序列 (b) 0pts
挺人类智慧的,其实转化以后并不难写,但是我被Hack控了一晚上,还把我1e-15的精度给卡了。。。;
考虑最后求的是乘积和,所以考虑将所有操作转化成乘积的形式;
对于赋值,最多只会赋值一次,是最大的那个,所以我们直接给它转化成加法;
对于加法,发现会从最大的开始加,所以对于一个加操作 $ y $,设所有加操作为 $ x $,那么它对乘积的贡献为 $ \frac{\sum_{i}^{x_i > y} + a_i + y}{\sum_{i}^{x_i > y} + a_i} $;
对于乘法,直接乘即可;
然后我们要处理一种情况:分母在模意义下为 $ 0 $;
那么我们发现,这只能对应加法操作,而且前面会出现一个分子为 $ 0 $ 的情况;
所以对于这种情况,我们看看前面还有多少个分子为 $ 0 $ 的情况,如果只剩一个,那么我们就把所有分子为 $ 0 $ 的数的分母和分母为 $ 0 $ 的数的分子对应合并起来,然后统计一下答案即可;
时间复杂度: $ \Theta(m \log(mod - 2)) $;
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const long long mod = 1e9 + 7;
int n, m;
long long a[500005], t[500005], x[500005], y[500005], ans[500005], ma[500005], res[500005], pos[500005];
vector<long long> v[500005];
struct sss{
long double val;
long long a, b, id;
bool operator <(const sss &A) const {
return (__int128)a * A.b > (__int128)A.a * b;
}
}e[500005];
int cnt;
set<int> s;
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;
}
int main() {
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> t[i] >> x[i] >> y[i];
if (t[i] == 1) ma[x[i]] = max(ma[x[i]], y[i]);
else if (t[i] == 2) {
v[x[i]].push_back(y[i]);
} else {
e[++cnt] = {1.00 * y[i], y[i], 1, x[i]};
}
}
for (int i = 1; i <= n; i++) {
if (ma[i] > a[i]) v[i].push_back(ma[i] - a[i]);
sort(v[i].begin(), v[i].end(), greater<long long>());
long long sum = 0;
for (int j = 0; j < v[i].size(); j++) {
e[++cnt] = {1.00 * (a[i] + sum + v[i][j]) / (a[i] + sum), a[i] + sum + v[i][j], a[i] + sum, i};
sum += v[i][j];
}
}
sort(e + 1, e + 1 + cnt);
long long ans = 1;
for (int i = 1; i <= n; i++) {
ans = ans * a[i] % mod;
}
cout << ans << ' ';
res[0] = 1;
for (int i = 1; i <= m; i++) {
if (i > cnt) {
for (int j = i; j <= m; j++) {
cout << ans << ' ';
}
return 0;
}
res[i] = res[i - 1];
if (e[i].a % mod != 0 && e[i].b % mod != 0) {
res[i] = res[i] * ksm(e[i].b % mod, mod - 2) % mod * (e[i].a % mod) % mod;
}
if (e[i].a % mod == 0) {
s.insert(i);
pos[e[i].id] = i;
}
if (e[i].b % mod == 0) {
e[i].b = e[pos[e[i].id]].b;
long long p = 1;
if (s.size() == 1) p = res[*s.begin() - 1];
ans = p * ksm(e[i].b % mod, mod - 2) % mod * (e[i].a % mod) % mod;
if (s.size() > 1) {
cout << 0 << ' ';
s.erase(prev(s.end()));
continue;
}
for (int j = *s.begin() + 1; j < i; j++) {
if (!(e[j].a % mod) || !(e[j].b % mod)) continue;
ans = ans * ksm(e[j].b % mod, mod - 2) % mod * (e[j].a % mod) % mod;
}
s.erase(prev(s.end()));
cout << ans << ' ';
continue;
}
if (i <= cnt) ans = ans * ksm(e[i].b % mod, mod - 2) % mod * (e[i].a % mod) % mod;
cout << ans << ' ';
}
return 0;
}
字符串 (d) 10pts
数据结构题,但是关键是想到数据结构;
考虑这个东西怎么统计,不难发现,我们按它给的字符串顺序对这个原序列进行统计,那么我们的答案即为相邻两个数 $ i, j, i \leq j $ 的个数 $ + 1 $;
对于这个东西,我们发现 $ k \leq 10 $,所以我们可以暴力 $ \Theta(k^2) $ 统计所有相邻的对数出现的个数并放在线段树上,修改直接 $ \Theta(k^2 \log n) $ 暴修,查询直接查询所有 $ \Theta(k^2) $ 个对的出现次数和;
时间复杂度:$ \Theta(mk^2 \log n) $;
主要是不要想偏,并不难实现;
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n, m, k;
char s[500005], c[28];
int su[11][11], ans;
vector<int> v, a;
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, sum[11][11], lval, rval, lz;
}tr[800005];
inline void w(int id, int x) {
tr[id].lz += x; tr[id].lz %= k;
tr[id].lval += x; tr[id].rval += x; tr[id].lval %= k; tr[id].rval %= k;
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
su[i][j] = tr[id].sum[i][j];
tr[id].sum[i][j] = 0;
}
}
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
tr[id].sum[(i + x) % k][(j + x) % k] += su[i][j];
}
}
}
inline void push_up(int id) {
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
tr[id].sum[i][j] = tr[ls(id)].sum[i][j] + tr[rs(id)].sum[i][j];
}
}
tr[id].lval = tr[ls(id)].lval;
tr[id].rval = tr[rs(id)].rval;
tr[id].sum[tr[ls(id)].rval][tr[rs(id)].lval]++;
}
inline void push_down(int id) {
if (tr[id].lz) {
w(ls(id), tr[id].lz);
w(rs(id), 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].lval = tr[id].rval = (s[l] - 'a');
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 x) {
if (tr[id].l >= l && tr[id].r <= r) {
w(id, x);
return;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (l <= mid) add(ls(id), l, r, x);
if (r > mid) add(rs(id), l, r, x);
push_up(id);
}
void ask(int id, int l, int r) {
if (tr[id].l == l && tr[id].r == r) {
a.push_back(id);
for (int i = v.size() - 1; i >= 0; i--) {
for (int j = i; j >= 0; j--) {
ans += tr[id].sum[v[i]][v[j]];
}
}
return;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (r <= mid) ask(ls(id), l, r);
else if (l > mid) ask(rs(id), l, r);
else {
ask(ls(id), l, mid);
ask(rs(id), mid + 1, r);
}
}
}
int main() {
freopen("d.in", "r", stdin);
freopen("d.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
cin >> (s + 1);
SEG::bt(1, 1, n);
int o, l, r, x;
for (int i = 1; i <= m; i++) {
cin >> o >> l >> r;
if (o == 1) {
cin >> x;
SEG::add(1, l, r, x);
}
if (o == 2) {
cin >> (c + 1);
v.clear();
a.clear();
for (int j = 1; j <= k; j++) {
v.push_back((c[j] - 'a'));
}
ans = 0;
SEG::ask(1, l, r);
for (int y = 1; y < a.size(); y++) {
for (int j = v.size() - 1; j >= 0; j--) {
for (int z = j; z >= 0; z--) {
if (v[j] == SEG::tr[a[y - 1]].rval && v[z] == SEG::tr[a[y]].lval) {
ans++;
break;
}
}
}
}
cout << ans + 1 << '\n';
}
}
return 0;
}