SCZ Round 2021

教练找到的奇怪的模拟赛,2h 绝杀 rk5 QwQ

\(\mathcal A\)

已知 \(3\) 个正整数 \(a,b,c\),求 \(3\) 个正整数 \(u,v,w\) 满足:

  • \(u+v=a\)
  • \(u-v=b\)
  • \(u\oplus v\oplus w= a\oplus b\oplus c\)

简单模拟 + 判断无解。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

LL read(){
	LL x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int main() {
	LL T = read();
	while(T --) {
		LL a = read(), b = read(), c = read();
		if((a + b) & 1) {puts("-1"); continue;}
		LL u, v, w;
		u = (a + b) >> 1;
		v = (a - b) >> 1;
		w = a ^ b ^ c ^ u ^ v;
		if(u <= 0 || v <= 0 || w <= 0) puts("-1");
		else printf("%lld %lld %lld\n", u, v, w);
	}
	return 0;
}

\(\mathcal B\)

给定 \(n-k+1\) 个在长度为 \(n\) 的序列上的,长度为 \(k\) 的连续区间的最小值。

求唯一确定的数的个数,必定有解,所有数字还会有上限 \(M\)

对于相邻的两个数 \(a_i,a_{i+1}\)

  1. \(a_i>a_{i-1}\):那么 \(ans_{i-1}=a_{i-1}\)
  2. \(a_i<a_{i-1}\):那么 \(ans_{i+k-1}=a_i\)

若相等则得不到什么有效信息,若 \(a_i=M\) 则该区间一定都为 \(M\),差分做一下即可。

还要特判 \(k=1\) 的情况,否则荣获 90pts。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1e6 + 10;
int n, k, m, a[N], ans[N], p[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int main() {
	n = read(), k = read(), m = read();
	for(int i = 1; i <= n - k + 1; i ++) a[i] = read();
	if(k == 1) {
		printf("%d\n", n);
		for(int i = 1; i <= n; i ++) printf("%d %d\n", i, a[i]);
		puts(""); return 0;
	}
	for(int i = 1; i <= n - k + 1; i ++) {
		if(i != 1 && a[i] > a[i - 1]) ans[i - 1] = a[i - 1];
		if(i != 1 && a[i] < a[i - 1]) ans[i + k - 1] = a[i];
		if(a[i] == m) p[i] ++, p[i + k] --;
	}
	for(int i = 1; i <= n; i ++) {
		p[i] += p[i - 1];
		if(p[i]) ans[i] = m;
	}
	int sum = 0;
	for(int i = 1; i <= n; i ++) if(ans[i]) sum ++;
	printf("%d\n", sum);
	for(int i = 1; i <= n; i ++) if(ans[i]) printf("%d %d\n", i, ans[i]);
	return 0;
}

\(\mathcal C\)

每个物品有两个权值 \(a,w\),要求 \(\sum w\leq C\)

对一个有 \(m\) 个物品的背包,代价为 \(\sum [a\geq m]\),求代价最大值。

单调性显然,直接二分答案即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 5e5 + 10;
int n, C;
struct Node{int a, w;} p[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(Node x, Node y) {return x.w < y.w;}

bool Chck(int m) {
	int num = 0; LL sum = 0;
	for(int i = 1; i <= n; i ++)
		if(p[i].a >= m) {
			sum += p[i].w;
			num ++;
			if(sum > C) return false;
			if(num >= m) return true;
		}
	return false;
}

int main() {
	n = read(), C = read();
	for(int i = 1; i <= n; i ++) p[i].a = read(), p[i].w = read();
	sort(p + 1, p + n + 1, cmp);
	int l = 0, r = n;
	while(l < r) {
		int mid = (l + r + 1) >> 1;
		if(Chck(mid)) l = mid;
		else r = mid - 1;
	}
	printf("%d\n", l);
	return 0;
}

\(\mathcal D\)

给定一棵树,每个节点有权值 \(0/1/2\),要求构造一个逆序对最小序列。

构造方法为,一个节点没有父亲时,它可以被加入序列,然后被删去。

还有两档数据分治的分,但都很简单。

这种父亲选了儿子才能选的题目有固定套路,就是可以贪心知道某个节点在父亲被选择后一定第一个选择。

那么直接用并查集 + set 进行 \(n-1\) 次合并就可以得到答案,贪心方法就直接比较两次顺序不同的逆序对。

考场正解 + 用两个堆代替 set 导致挂到地上 QwQ

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;

typedef long long LL;
const int N = 4e5 + 10;
int n, p[N], v[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int c[N];

void Add(int x, int v) {x ++; for(; x <= N - 10; x += x & -x) c[x] += v;}
int Ask(int x) {x ++; LL sum = 0; for(; x; x -= x & -x) sum += c[x]; return sum;}

void WorkA() {
	LL ans = 0;
	for(int i = n; i >= 1; i --) ans += Ask(v[i] - 1), Add(v[i], 1);
	printf("%lld\n", ans);
}

void WorkB() {
	LL ans = 0;
	for(int i = 2; i <= n; i ++) if(v[i] < v[1]) ans ++;
	printf("%lld\n", ans);
}

int fa[N]; LL ans = 0;

struct Node{
	int x, y, z;
	bool operator < (const Node &a) const {
		LL L = (LL)y * a.x + (LL)z * a.y + (LL)z * a.x;
		LL R = (LL)a.y * x + (LL)a.z * y + (LL)a.z * x;
		return L < R;
	}
} f[N];

struct Set{
	int x;
	bool operator < (const Set &a) const {return f[x] < f[a.x] ? 1 : (f[a.x] < f[x] ? 0 : x < a.x);}
};
set<Set> s;

int Get(int x) {
	if(x == fa[x]) return x;
	return fa[x] = Get(fa[x]);
}

void Merge(int u, int v) {
	ans += (LL)f[v].y * f[u].x + (LL)f[v].z * f[u].x + (LL)f[v].z * f[u].y;
	f[v].x += f[u].x;
	f[v].y += f[u].y;
	f[v].z += f[u].z;
	fa[u] = v;
}

void WorkC() {
	for(int i = 1; i <= n; i ++) {
		f[i].x += v[i] == 0;
		f[i].y += v[i] == 1;
		f[i].z += v[i] == 2;
		s.insert((Set){i}); fa[i] = i;
	}
	for(int i = 1; i < n; i ++) {
		int u;
		while(true) {
			u = s.begin() -> x;
			if(Get(p[u])) break;
			s.erase(s.begin());
		}
		int v = Get(p[u]);
		s.erase((Set){u});
		s.erase((Set){v});
		Merge(u, v); s.insert((Set){v});
	}
	printf("%lld\n", ans);
}

int main() {
	n = read();
	bool A = true, B = true;

	for(int i = 2; i <= n; i ++) {
		p[i] = read();
		if(p[i] != i - 1) A = false;
		if(p[i] != 1) B = false; 
	}
	for(int i = 1; i <= n; i ++) v[i] = read();

	if(A) WorkA();
	else if(B) WorkB();
	else WorkC();
	return 0;
}

\(\mathcal E\)

已知 \(x=v_0t+\frac{1}{2}at^2\),给定初始的 \(a,v_0\),以及 \(x\),以及 \(n\) 天的加成选择,求到达 \(x\) 的最小 \(t\)

\(i\) 天可以选择给 \(v_0+a_i\),也可选择给 \(a+b_i\),也可以都不加。

若连续加了 \(k\) 次及以上,每加一次后面的 \(a_i,b_i\) 都要 \(/2\)

意思时,若 \(k=2\). 表示加成:

---.---...----..--- 最后会 \(/2\) 三次。

答案 \(>10000\) 输出 Defeat

最难想到的是二分答案 \(t\),求能否 \(\geq x\)

然后就直接设 \(f(i,j,0/1)\) 表示前 \(i\) 次训练,\(/2\)\(j\) 次,第 \(i\) 次是否是 \(/2\) 态的最大位移。

方程写注释里了,可以简单用单调队列优化,时间复杂度 \(O(n\log n\log V)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef double DB;
typedef long long LL;
const int N = 2e5 + 10;
const DB INF = 1e18;

int n, K, A, B; LL S;
int a[N], b[N];
DB s[N][35], f[N][35][2];
// f[i][j][0] = max(f[i - 1][j][1], s[i - 1][j] + f[k][j][0] - s[k][j]) (i - K <= k <= i - 1)
// f[i][j][1] = max(f[i - 1][j - 1][1] + s[i][j - 1] - s[i - 1][j - 1], f[i - K - 1][j - 1][0] + s[i][j - 1] - s[i - K - 1][j - 1])
struct Queue {
	int L, R, q[N]; DB val[N];
	void Push(int x, DB v) {
		while(L <= R && val[R] <= v) R --;
		q[++ R] = x, val[R] = v;
	}
	DB Front(int x) {
		while(L <= R && q[L] < x - K - 1) L ++;
		return val[L]; 
	}
} Q[35];

LL read(){
	LL x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool Chck(DB t) {
	DB two = 1.0;
	for(int i = 0; i <= 30; i ++) {
		s[0][i] = 0.0;
		for(int j = 1; j <= n; j ++)
			s[j][i] = s[j - 1][i] + max(a[j] * t, b[j] * t * t / 2.0) / two;
		two *= 2.0;
		Q[i].L = 1, Q[i].R = 0;
	}
	Q[0].Push(0, 0.0);
	for(int i = 1; i <= n + 1; i ++) {
		for(int j = 0; j <= 30; j ++) {
			f[i][j][0] = f[i - 1][j][1];
			f[i][j][1] = 0.0;

			DB now = Q[j].Front(i);
			if(Q[j].L <= Q[j].R) f[i][j][0] = max(f[i][j][0], s[i - 1][j] + now);
			if(i == n + 1) continue;
			
			if(j) {
				f[i][j][1] = f[i - 1][j - 1][1] + s[i][j - 1] - s[i - 1][j - 1];
				if(i > K) f[i][j][1] = max(f[i][j][1], f[i - K - 1][j - 1][0] + s[i][j - 1] - s[i - K - 1][j - 1]);
			}
			Q[j].Push(i, f[i][j][0] - s[i][j]);
		}
	}
	DB now = 0.0;
	for(int j = 0; j <= 30; j ++) now = max(now, f[n + 1][j][0]);
	return (now + A * t + B * t * t / 2.0 >= S);
}

int main() {
	n = read(), K = read();
	S = read(), A = read(), B = read();
	for(int i = 1; i <= n; i ++)
		a[i] = read(), b[i] = read();

	DB l = 0.0, r = 10001.0;
	for(int i = 1; i <= 30; i ++) {
		DB mid = (l + r) / 2.0;
		if(Chck(mid)) r = mid; else l = mid;
	}
	if(l > 10000.0) puts("Defeat");
	else printf("%.2lf\n", l);
	return 0;
}
posted @ 2021-09-27 20:32  LPF'sBlog  阅读(82)  评论(0编辑  收藏  举报