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('
')
/*----------------------------------------------------------*/
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

题目要求:

[sum_{y = L}^R [gcd(y - k, y + k) = 1] ]

首先 有:

[gcd(a, b) = gcd(b, a - b) ]

考虑题中原式取值进行转化 设 (i = y - k) 有:

[sum_{i = L}^{R - 2k} [gcd(i, 2k) = 1] ]

这个式子的含义就是求 ([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] o 3, [1, 1] o 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;}
原文地址:https://www.cnblogs.com/blank-space-/p/15310266.html