9.18 校内模拟赛 题解报告
9.18 校内模拟赛 题解报告
扯
T1 sb dp
T2 sb 数论
T3 分析 + 数据结构
BS: 做不出 sb 题的 sb
大概这场考试就是这样...
关于考试过程以及一些题外话
传说中的送分题 拿不到分系列
T1 一开始的时候先看到前面 \(O(2^n)\) 的暴力 再看数据以为是个结论题 然后感觉好像可以直接 dp 复杂度 \(O(n)\) 好像能过 稍微卡了一下 感觉没什么问题就扔了
T2 ...
BS: 我曾尝试做过
T3 最开始想到的是回滚莫队... 然后看到区间和就没辙了 类比回滚算贡献的方法把 \(O(n^2)\) 的部分写了就滚粗了
最后补了一个 T2 的 \(O(R - L)\) 的暴力
这个 T3 真的满脑子回滚莫队 根本没发现单调性
得分情况
100 + 30 + 48 = 178
所以这个 \(48\) 是个啥...
题解
显然没有题解
T1 zero
状态: \(f_{i, 0/1}\) 表示前 \(i\) 位置运算得到 \(0/1\) 的方案数
转移考虑当前位选择的数造成的运算结果
代码
/*
Source:
*/
#include<cstdio>
#include<cstring>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
template <typename T> inline T Pow(T x) {return x * x;}
template <typename T> inline T Abs(T x) {return x < 0 ? -x : x;}
template <typename T> inline T Min(T x, T y) {return x < y ? x : y;}
template <typename T> inline T Max(T x, T y) {return x > y ? x : y;}
template <typename T> inline void Swap(T x, T y) {x ^= y ^= x ^= y;}
/*----------------------------------------------------------*/
inline void File() {
freopen("zero.in", "r", stdin);
freopen("zero.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, f[D][2], pa[25], ans;
char s[D];
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool check() {
int res = pa[0];
for(int i = 1; i ^ n + 1; ++i)
if(s[i] == '&') res &= pa[i];
else if(s[i] == '|') res |= pa[i];
else if(s[i] == '^') res ^= pa[i];
return res;
}
void print() {
for(int i = 0; i ^ n + 1; ++i) Print(pa[i]), pt; pn;
}
void dfs1(int t) {
if(t == n + 1) {if(check()) ans++; return ;}
pa[t] = 0; dfs1(t + 1); pa[t] = 1; dfs1(t + 1);
}
void work1() {dfs1(0); Print(ans);}
void work2() {
f[1][0] = f[1][1] = 1;
for(int i = 1; i ^ n + 2; ++i)
if(s[i] == '&') f[i + 1][0] = (f[i + 1][0] + f[i][0] + f[i][1] + f[i][0]) % mod, f[i + 1][1] = (f[i + 1][1] + f[i][1]) % mod;
else if(s[i] == '|') f[i + 1][0] = (f[i + 1][0] + f[i][0]) % mod, f[i + 1][1] = (f[i + 1][1] + f[i][0] + f[i][1] + f[i][1]) % mod;
else if(s[i] == '^') f[i + 1][0] = (f[i + 1][0] + f[i][0] + f[i][1]) % mod, f[i + 1][1] = (f[i + 1][1] + f[i][0] + f[i][1]) % mod;
Print(f[n + 1][1] % mod);
}
void Main() {
File();
// freopen("a.in", "r", stdin);
n = read(); scanf("%s", s + 1);
// if(n <= 20) work1();
// else work2();
work2();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
T2 prime
题目要求:
首先 有:
考虑题中原式取值进行转化 设 \(i = y - k\) 有:
这个式子的含义就是求 \([L, R - 2k]\) 中与 \(2k\) 互质的数的个数 考虑将所有与 \(2k\) 不互质的数筛掉
对 \(2k\) 进行质因数分解 取出其所有质因数 然后将 \([L, R - 2k]\) 范围内还有这些质因数的数都筛掉
对于每个质因数考虑取与不取两种情况 由于 \(k\) 只有 \(10^{13}\) 所以其质因数不会超过 \(15\) 个
对于区间中会被具有多个 \(2k\) 的质因子的数筛去 容斥一下即可
代码
/*
Source: 小y的质数
*/
#include<cstdio>
#define int long long
/*----------------------------------------------------------*/
int L, R, K, p[20], cnt, ans;
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void pre(int x) {
for(int i = 2; i * i <= x; ++i) if(!(x % i))
{
p[++cnt] = i;
while(!(x % i)) x /= i;
}
if(x > 1) p[++cnt] = x;
}
void dfs(int t, int x, int sum) {
if(t == cnt + 1) {int k = sum & 1 ? 1 : -1; if(sum) ans += k * (R / x - (L - 1) / x); return ;}
dfs(t + 1, x, sum); dfs(t + 1, x * p[t], sum + 1);
}
void Main() {
L = read(); R = read(); K = read() << 1; R -= K;
if(R < L) {Print(0); return ;} if(!L) ++L;
pre(K); dfs(1, 1, 0); Print(R - L + 1 - ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
T3 mex
如果确定某一点作为区间的左端点 将区间向右侧延伸 则随着区间的延伸 区间的 \(mex\) 是单调上升的
首先求出所有以 \(1\) 作为左端点的区间的 \(mex\) 此时求和即为所有以 \(1\) 为左端点区间的贡献 而如果将 \(a_1\) 的贡献去除 得到的就是所有以 \(2\) 作为左端点的区间的贡献总和
考虑如何去除 \(a_1\) 的贡献 将所有区间的左端点转移到 \(2\) 设 \(a_1 = x\) 若是将 \(a_1\) 删去 一定不会影响到下一个包含与 \(a_1\) 相等数的位置 不妨设 \(1\) 之后第一个与 \(a_1\) 相等的位置为 \(pos\) 则删除 \(a_1\) 一定不会影响右端点在 \([pos, n]\) 的区间(左端点在 \(2\)) 再考虑对区间 \([2, pos - 1]\) 的影响 现在已经维护出了所有以 \(1\) 为左端点的区间 这些区间的 \(mex\) 值是单调递增的 在这些 \(mex\) 值中 会被影响到的只有大于 \(a_1\) 的值 只需要将所有的这些值直接改为 \(a_1\) 就好了 这时维护的就是以 \(2\) 作为区间左端点的所有区间 求和计入答案
按照上面的方法 将所有数逐个删除 维护删除后区间的 \(mex\) 和 求和为答案
总结一下的话
一开始将以 \(1\) 为区间左端点的区间的 \(mex\) 值求出来 直接对应到下标 如 \([1, 3] \to 3, [1, 1] \to 1\) 这样需要维护的东西就是区间求和 区间赋值 查询区间中大于某个数的值 这些操作显然是都可以在线段树上维护的
代码
/*
Source: mex
*/
#include<cstdio>
#define int long long
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
template <typename T> inline T Min(T x, T y) {return x < y ? x : y;}
template <typename T> inline T Max(T x, T y) {return x > y ? x : y;}
/*----------------------------------------------------------*/
int n, a[B], mex[B], cnt[B], K, pos[B], pre[B], ans;
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define len (t[p].r - t[p].l + 1)
#define mid (t[p].l + t[p].r >> 1)
struct node {
int l, r, sum, max, lzy;
node() {l = r = sum = max = 0; lzy = -1;}
} t[B << 2];
void push_up(int p) {t[p].sum = t[ls(p)].sum + t[rs(p)].sum; t[p].max = Max(t[ls(p)].max, t[rs(p)].max);}
void build(int p, int l, int r) {
t[p].l = l; t[p].r = r; if(l == r) {t[p].sum = t[p].max = mex[l]; return ;}
build(ls(p), l, mid); build(rs(p), mid + 1, r); push_up(p);
}
void f(int p, int k) {t[p].lzy = t[p].max = k; t[p].sum = k * len;}
void push_down(int p) {f(ls(p), t[p].lzy); f(rs(p), t[p].lzy); t[p].lzy = -1;}
void up_date(int p, int l, int r, int k) {
if(l <= t[p].l && t[p].r <= r) {f(p, k); return ;}
if(~t[p].lzy) push_down(p);
if(l <= mid) up_date(ls(p), l, r, k); if(r > mid) up_date(rs(p), l, r, k);
push_up(p);
}
int query(int p, int k) {
if(t[p].l == t[p].r) return t[p].l;
if(k < t[ls(p)].max) return query(ls(p), k); else return query(rs(p), k);
}
}
void Main() {
n = read(); mex[n + 1] = INF; pre[0] = n + 1;
for(int i = 1; i ^ n + 1; ++i) a[i] = read(), cnt[a[i]]++, pre[i] = n + 1;
for(int i = 0; i ^ n + 1; ++i) if(!cnt[i]) {K = i; break;}
for(int i = n; i; --i)
{
mex[i] = K; if(!--cnt[a[i]]) K = Min(K, a[i]);
pos[i] = pre[a[i]]; pre[a[i]] = i;
}
Seg::build(1, 1, n); ans = Seg::t[1].sum;
for(int i = 1; i ^ n + 1; ++i)
{
Seg::up_date(1, i, i, 0);
if(Seg::t[1].max > a[i])
{
int x = Seg::query(1, a[i]);
if(x < pos[i]) Seg::up_date(1, x, pos[i] - 1, a[i]);
}
ans += Seg::t[1].sum;
}
Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}