2021.11.09模拟赛

本文介绍了两道编程题目,分别是关于构造字符串的优化策略和线段树的应用。第一题中,通过分析给出了求解最大值的数学公式,并提供了构造字符串的算法。第二题中,原题解法在考场上效率较低,正解则是通过建立区间树来动态维护颜色信息,实现了高效查询和更新。
摘要由CSDN通过智能技术生成

T1 string

  一道构造题。大概是这样的,首先 k 等于 1 的时候,如果 1 在第 i 个位置那么答案就是 i ( n − i + 1 ) i(n-i+1) i(ni+1),如果 k 等于 2,两个 1 分别在 i j然后 i < j 。那么答案就是 ( − i + j ) ( n + i − j + 1 ) (-i+j)(n+i-j+1) (i+j)(n+ij+1),如果 k 等于 3,三个 1 分别在 i, j, k 且 i < j < k,那么答案就是 ( i − j + k ) ( n − i + j − k + 1 ) (i-j+k)(n-i+j-k+1) (ij+k)(ni+jk+1),…

  然后就找到规律就是说令 a 为 1 的下标:
T = ( − 1 ) k ∑ i = 1 k ( − 1 ) i a i T = (-1)^k\sum_{i=1}^k(-1)^ia_i T=(1)ki=1k(1)iai

  答案就是:
a n s = T ( n − T + 1 ) = − T 2 + ( n + 1 ) T ans = T(n - T + 1) = -T^2 + (n+1)T ans=T(nT+1)=T2+(n+1)T

  然后我们就可以把这玩意儿看成一个关于 T 的二次函数,求出最大值:
a n s = − ( T − n + 1 2 ) 2 + ( n + 1 ) 2 4 → a n s m a x = ( n + 1 ) 2 4 ans = -(T-\frac{n+1}{2})^2 + \frac{(n+1)^2}{4} \rightarrow ans_{max} = \frac{(n+1)^2}{4} ans=(T2n+1)2+4(n+1)2ansmax=4(n+1)2

  然后就要构造一个串让这个串的 T 等于 n + 1 2 \frac{n+1}{2} 2n+1 就好了。一种简单的构造方法就是先在最前面连续放 k − 1 k - 1 k1 个 1,然后再在后面找一个位置让它们算出来是 T 就好了。最后还要特判一下 k = 0 的情况。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100100
#define endl '\n'

int ans[MAXN] = { 0 };
int n = 0; int k = 0;

int main(){
	scanf("%d%d", &n, &k);
	if(k == 0){
		puts("0");
		for(int i = 1; i <= n; i++) cout << 0 << ' ';
		puts("");
		return 0;
	}
	cout << 1ll * (n + 1) * (n + 1) / 4 << endl;
	for(int i = 1; i < k; i++) ans[i] = 1;
	ans[(n + k + 1 - (k & 1)) / 2] = 1;
	for(int i = 1; i <= n; i++) cout << ans[i] << ' ';
	puts("");
	return 0;
}

T2 artist

  一道线段树的题,在考场上打了一个模拟 O ( n 3 ) O(n^3) O(n3) 然后得了 10 分qwq。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 500500
#define endl '\n'

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

struct Trange{
	int l; int r;
};

int q = 0;
int n = 0; int m = 0;
int c[MAXN] = { 0 };
int l[MAXN] = { 0 };
Trange r[MAXN] = { 0 };

bool check(int idx){
	int tong[MAXN] = { 0 };
	for(int i = r[idx].l; i <= r[idx].r; i++)
		if(!tong[c[i]]) tong[c[i]]++;
		else return false;
	return true;
}

int main(){
	freopen("artist.in", "r", stdin);
	freopen("artist.out", "w", stdout);
	n = in; m = in; q = in;
	for(int i = 1; i <= n; i++) c[i] = in;
	for(int i = 1; i <= m; i++){
		r[i].l = in; r[i].r = in;
	}
	for(int i = 1; i <= m; i++) l[i] = -1;
	for(int i = 1; i <= m; i++) if(check(i)) l[i] = 0;
	for(int i = 1; i <= q; i++){
		int x = in; int y = in;
		c[x] = y;
		for(int j = 1; j <= m; j++)
			if(check(j) and l[j] == -1) l[j] = i;
	}
	for(int i = 1; i <= m; i++) if(l[i] == -1) l[i] = m + i;
	int ans = 0;
	for(int i = 1; i <= m; i++) ans ^= l[i];
	cout <<  ans << endl;
	return 0;
}

  下面是正解:

  因为每个区间要么相互包含要么不想交,那我们可以对所有区间建一棵树,范围大的区间为父节点,范围小的区间为子节点。然后每一个节点内就存储对应区间内的颜色信息。因为只有在儿子被处理成全都不一样之后父亲才可能被处理成全都不一样。所以每次修改我们只修改儿子。等到儿子符合条件,再暴力将儿子的部分全部复制给父亲。

  对于询问来说,我们可以对每个区间维护一个 last 数组,last[i] 表示 i 之前的第一个与 i 相同颜色的下标。这样一个区间内颜色互不相同的个数就等价于 ( r − l + 1 ) − max ⁡ i = l r { l a s t i } (r-l+1) - \max\limits_{i=l}^r \lbrace last_i\rbrace (rl+1)i=lmaxr{lasti},这玩意儿就可以用一个 set 和线段树来维护。

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second 
#define in read()
#define MAXN 500500
typedef pair<int, int> pii;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int q = 0;
int n = 0; int m = 0;
int pre[MAXN] = { 0 };
int fa[MAXN] = { 0 };
int c[MAXN] = { 0 };
int d[MAXN] = { 0 };
int l[MAXN] = { 0 };
pii b[MAXN];
set<int> co[MAXN];
set<pair<pii, int> > s;

struct Tnode{
	int l, r;
	int dat;
}t[4 * MAXN];
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
inline void pushup(int p){
	t[p].dat = max(t[ls(p)].dat, t[rs(p)].dat);
}
void build(int p, int l, int r){
	t[p].l = l; t[p].r = r;
	if(l == r){
		t[p].dat = pre[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(ls(p), l, mid);
	build(rs(p), mid + 1, r);
	pushup(p);
}
void update(int p, int index, int val){
	if(t[p].l == t[p].r){
		t[p].dat = val;
		return;
	}
	int mid = (t[p].l + t[p].r) >> 1;
	if(index <= mid) update(ls(p), index, val);
	else update(rs(p), index, val);
	pushup(p);
}
int query(int p, int l, int r){
	if(l <= t[p].l and t[p].r <= r) return t[p].dat;
	int mid = (t[p].l + t[p].r) >> 1; int ans = -1;
	if(l <= mid) ans = max(ans, query(ls(p), l, r));
	if(r > mid)  ans = max(ans, query(rs(p), l, r));
	return ans;
}

inline bool isin(pii x, pii y){
	return y.fi <= x.fi and x.se <= y.se;
}
inline bool comp(pair<pii, int> x, pair<pii, int> y){
	return x.fi.fi == y.fi.fi ? x.fi.se > y.fi.se : x.fi.fi < y.fi.fi;
}
void init(){
	for(int i = 1; i <= n; i++) co[i].insert(0);
	for(int i = 1; i <= n; i++){
		pre[i] = *(co[c[i]].rbegin());
		co[c[i]].insert(i);
	}
	build(1, 1, n);
	pair<pii, int> temp[MAXN];
	b[0].fi = -1; b[0].se = n + 1;
	for(int i = 0; i <= m; i++){
		temp[i].fi = b[i]; temp[i].se = i;
	}
	sort(temp, temp + m + 1, comp);
	stack<int> stk; stk.push(0);
	for(int i = 1; i <= m; i++){
		while(!isin(temp[i].fi, temp[stk.top()].fi)) stk.pop();
		fa[temp[i].se] = temp[stk.top()].se; d[fa[temp[i].se]]++;
		stk.push(i);
	}
	for(int i = 1; i <= m; i++){
		if(!d[i]) s.insert({b[i], i});
	}
}

void modify(int x, int d){
	auto p = co[c[x]].lower_bound(x);
	auto bf = p; auto nx = p; nx++; bf--;
	if(nx != co[c[x]].end()) update(1, *nx, *bf);
	co[c[x]].erase(x);
	c[x] = d;
	nx = co[c[x]].lower_bound(x);
	bf = nx; bf--; update(1, x, *bf);
	if(nx != co[c[x]].end()) update(1, *nx, x);
	co[c[x]].insert(x);
}

int queryB(int x){
	auto p = s.upper_bound({{x, n + 1}, m + 1});
	if(p == s.begin()) return -1;
	p--; auto B = *p;
	if(B.fi.fi <= x and x <= B.fi.se) return B.se;
	return -1;
}

void check(int id, int i){
	if(id <= 0) return;
	if(query(1, b[id].fi, b[id].se) >= b[id].fi) return;
	l[id] = i; s.erase({b[id], id});
	if(!(--d[fa[id]])){
		s.insert({b[fa[id]], fa[id]}); check(fa[id], i);
	}
}

int main(){
	n = in; m = in; q = in;
	for(int i = 1; i <= n; i++) c[i] = in;
	for(int i = 1; i <= m; i++){
		b[i].fi = in; b[i].se = in; l[i] = -1;
	}
	init();
	vector<int> leaf;
	for(auto v : s) leaf.push_back(v.se);
	for(int v : leaf) check(v, 0);
	for(int i = 1; i <= q; i++){
		int x = in; int y = in;
		modify(x, y); check(queryB(x), i);
	}
	int ans = 0;
	for(int i = 1; i <= m; i++){
		if(l[i] == -1) ans ^= m + i;
		else ans ^= l[i];
	}
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值