CSP-S模拟17

挂分原因: 数组开到 \(n\) ,读入 \(2n ,3n\)...

A. 最大匹配

问题转化选择 \(n\)\(max(a_i, b_i)\) 剩下选\(-min(a_i, b_i)\)

于是可反悔贪心直接莽

其他人的做法是分情况讨论证明按照 \(a + b\) 排序,首尾配对

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

const int maxn = 300005;
int n;
struct node{
	int a, b;
	friend bool operator < (const node x, const node y){return x.a > y.a;}
}d[maxn];
const ll inf = 0x3f3f3f3f3f;
typedef pair <ll, int> pli;
priority_queue<pli>q1, q2, q3;
int vis[maxn];
int main(){
	n = read();
	for(int i = 1; i <= n + n; ++i){
		int x = read(), y = read();
		d[i].a = max(x, y); d[i].b = -min(x, y);
	}
	ll ans = 0;
	sort(d + 1, d + n + n + 1);
	for(int i = 1; i <= n; ++i)ans += d[i].a, q1.push(pli(d[i].b - d[i].a, i)), vis[i] = 1;
	for(int i = 1; i <= n; ++i)q2.push(pli(d[i + n].b, i + n)), q3.push(pli(d[i + n].a, i + n));
	q1.push(pli(-inf, 0));
	q2.push(pli(-inf, 0));
	q3.push(pli(-inf, 0));
	for(int i = 1; i <= n; ++i){
		while(!q1.empty() && vis[q1.top().second] != 1 && q1.top().second)q1.pop();
		while(!q2.empty() && vis[q2.top().second] && q2.top().second) q2.pop();
		while(!q3.empty() && vis[q3.top().second] && q3.top().second)q3.pop();
		ll v1 = q2.top().first, v2 = q3.top().first + q1.top().first;
		if(v1 >= v2){
			ans += v1;
			vis[q2.top().second] = 2;
			q2.pop();
		}else{
			ans += v2;
			vis[q1.top().second] = 2;
			q1.pop();
			vis[q3.top().second] = 1;
			q1.push(pli(d[q3.top().second].b - d[q3.top().second].a, q3.top().second));
			q3.pop();
		}
	}
	printf("%lld\n",ans);
	return 0;
}

B. 挑战ABC

大力分讨

每次操作只能增加一种字母的数量

所以整体分两种情况

  1. 两种字母小于 \(n\)

于是把大于 \(n\) 的那种取出多的部分,改成少的一种,再把多的改成另外一种

  1. 一种字母小于 \(n\)

假设另外两种 \(x, y\) 分别比 \(n\)\(a, b\), 那么如果能找到一段恰好出现 \(ax, by\) 的直接一次操作

否则,选择 \(x\) 出现 \(a\) 次, \(y\) 出现大于 \(b\) 次的,先改成 \(z\), 再把多的改回 \(y\)

如果 \(x\) 出现 \(a\) 次, \(y\) 小于 \(b\) 次就找 \(y\) 出现 \(b\) 次的

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 900005;

int n, m;
char c[maxn];
int sa[maxn], sb[maxn], sc[maxn];
bool check1(){
	int cnt = (sa[m] < n) + (sb[m] < n) +(sc[m] < n);
	if(cnt > 1)return false;
	int ca = sa[m] - n, cb = sb[m] - n, cc = sc[m] - n, pl = 1;
	if(sa[m] < n){
		int nb = 0, nc = 0;
		for(int i = 1; i <= m; ++i){
			nb += c[i] == 'B';
			nc += c[i] == 'C';
			if(nb < cb || nc < cc)continue;
			while(nb - (c[pl] == 'B') >= cb && nc - (c[pl] == 'C') >= cc && pl <= i){
				nb -= c[pl] == 'B'; nc -= c[pl] == 'C'; ++pl;
			}
			if(nb == cb && nc == cc){
				printf("1\n");
				printf("%d %d A\n", pl, i);
				return true;
			}
		}
		printf("2\n");
		for(int i = 1; i <= m; ++i)if(sb[i] == cb){
			if(sc[i] > cc){
				printf("%d %d A\n",1, i);
				printf("%d %d C\n",1, sa[m] - sa[i] + i - n);
				return true;
			}
		}
		for(int i = 1; i <= m; ++i)if(sc[i] == cc){
			if(sb[i] > cb){
				printf("%d %d A\n",1, i);
				printf("%d %d B\n",1, sa[m] - sa[i] + i - n);
				return true;
			}	
		}
	}
	if(sb[m] < n){
		int na = 0, nc = 0;
		for(int i = 1; i <= m; ++i){
			na += c[i] == 'A';
			nc += c[i] == 'C';
			if(na < ca || nc < cc)continue;
			while(na - (c[pl] == 'A') >= ca && nc - (c[pl] == 'C') >= cc && pl <= i){
				na -= c[pl] == 'A'; nc -= c[pl] == 'C'; ++pl;
			}
			if(na == ca && nc == cc){
				printf("1\n");
				printf("%d %d B\n", pl, i);
				return true;
			}
		}
		printf("2\n");
		for(int i = 1; i <= m; ++i)if(sa[i] == ca){
			if(sc[i] > cc){
				printf("%d %d B\n",1, i);
				printf("%d %d C\n",1, sb[m] - sb[i] + i - n);
				return true;
			}
		}
		for(int i = 1; i <= m; ++i)if(sc[i] == cc){
			if(sa[i] > ca){
				printf("%d %d B\n",1, i);
				printf("%d %d A\n",1, sb[m] - sb[i] + i - n);
				return true;
			}	
		}
	}
	if(sc[m] < n){
		int na = 0, nb = 0;
		for(int i = 1; i <= m; ++i){
			na += c[i] == 'A';
			nb += c[i] == 'B';
			if(na < ca || nb < cb)continue;
			while(na - (c[pl] == 'A') >= ca && nb - (c[pl] == 'B') >= cb && pl <= i){
				na -= c[pl] == 'A'; nb -= c[pl] == 'B'; ++pl;
			}
			if(na == ca && nb == cb){
				printf("1\n");
				printf("%d %d C\n", pl, i);
				return true;
			}
		}
		printf("2\n");
		for(int i = 1; i <= m; ++i)if(sa[i] == ca){
			if(sb[i] > cb){
				printf("%d %d C\n",1, i);
				printf("%d %d B\n",1, sc[m] - sc[i] + i - n);
				return true;
			}
		}
		for(int i = 1; i <= m; ++i)if(sb[i] == cb){
			if(sa[i] > ca){
				printf("%d %d C\n",1, i);
				printf("%d %d A\n",1, sc[m] - sc[i] + i - n);
				return true;
			}
		}
	}
	return false;
}
void solve2(){
	int pl = 1;
	printf("2\n");
	if(sa[m] > n){
		for(int i = 1; i <= m; ++i)if(sa[i] == sa[m] - n){
			printf("%d %d B\n", 1, i);
			printf("%d %d C\n", 1, sb[m] - sb[i] + i - n);
			return;
		}
	}	
	if(sb[m] > n){
		for(int i = 1; i <= m; ++i)if(sb[i] == sb[m] - n){
			printf("%d %d A\n", 1, i);
			printf("%d %d C\n", 1, sa[m] - sa[i] + i - n);
			return;
		}
	}
	if(sc[m] > n){
		for(int i = 1; i <= m; ++i)if(sc[i] == sc[m] - n){
			printf("%d %d A\n", 1, i);
			printf("%d %d B\n", 1, sa[m] - sa[i] + i - n);
			return;
		}
	}
}
int main(){
	scanf("%d",&n); m = n + n + n;
	scanf("%s",c + 1);
	for(int i = 1; i <= m; ++i){
		sa[i] = c[i] == 'A';
		sb[i] = c[i] == 'B';
		sc[i] = c[i] == 'C';
		sa[i] += sa[i - 1];
		sb[i] += sb[i - 1];
		sc[i] += sc[i - 1];
	}
	if(sa[m] == sb[m] && sa[m] == sc[m]){
		printf("0\n");
		return 0;
	}
	if(check1())return 0;
	solve2();
	return 0;
}

C. 三级跳

三元组 \((a, b, c)\) 可以得到 \(w_a,w_b\) 一定是区间 \([a,b]\) 的最大值,否则可以收缩区间,使得 \(c\) 的选择范围变大

于是考虑将三元组的贡献挂到 \(c\) 上,用线段树进行维护

发现这样我们能够通过查询区间限制 \(r\) ,但是 \(l\) 难以限制,于是从右侧向左侧扫描线

使用单调栈维护 \(a, b\), 发现这样的关系当 \(a\) 入栈时弹出了 \(b\) 或者 \(a\) 在栈里与 \(b\) 相邻,于是数对的数量级为 \(O(n)\)

在单调栈的同时根据 \(a, b\)算出合法的 \(c\) 的范围进行区间修改即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

const int maxn = 500005;
int a[maxn], n;

struct query{
	int l, r, id;
	friend bool operator < (const query &x, const query &y){
		return x.l > y.l;
	}
}q[maxn];
struct seg{
	struct node{
		ll tag, val, mx;
	}t[maxn << 2 | 1];
	void push_up(int x){
		t[x].mx = max(t[x << 1].mx, t[x << 1 | 1].mx);
		t[x].val = max(t[x << 1].val , t[x << 1 | 1].val);
	}
	void built(int x, int l, int r){
		if(l == r){
			t[x].mx = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void upd(int x, ll val){
		t[x].tag = max(t[x].tag, val);
		t[x].val = max(t[x].val, t[x].mx + val);
	}
	void push_down(int x){
		upd(x << 1, t[x].tag);
		upd(x << 1 | 1, t[x].tag);
		t[x].tag = 0;
	}
	void modify(int x, int l, int r, int L, int R, ll val){
		if(L > R)return;
		if(L <= l && r <= R){
			upd(x, val);
			return;
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L ,R, val);
		push_up(x);
	}
	ll query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].val;
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		ll ans = 0;
		if(L <= mid)ans = max(ans, query(x << 1, l, mid, L, R));
		if(R > mid)ans = max(ans, query(x << 1 | 1, mid + 1, r, L, R));
		return ans;
	}
}t;
int sta[maxn], top;
ll ans[maxn];
void pop(int x){
	t.modify(1, 1, n, sta[top] + sta[top] - x, n, a[sta[top]] + a[x]);
	--top;
}
void push(int x){
	if(top)t.modify(1, 1, n, sta[top] + sta[top] - x, n, a[sta[top]] + a[x]);
	sta[++top] = x;
}
int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	t.built(1, 1, n);
	int Q = read();
	for(int i = 1; i <= Q; ++i){
		q[i].l = read(); q[i].r = read(); q[i].id = i;
	}
	sort(q + 1, q + Q + 1);
	int pq = 1;
	for(int i = n; i >= 1; --i){
		while(top && a[sta[top]] <= a[i])pop(i);
		push(i);
		while(pq <= Q && q[pq].l >= i){
			ans[q[pq].id] = t.query(1, 1, n, q[pq].l, q[pq].r);
			++pq;
		}
	}
	for(int i = 1; i <= Q; ++i)printf("%lld\n",ans[i]);
	return 0;
}

D. 经典线性基

简单学习了一下线性基

以下做法目前存在问题

\([l, r]\) 拆分成若干 \([x \times 2^k, (x + 1) \times 2^k)\) 的形式

每次处理这样一个区间

如果 \(k <= 15\) (题解说是\(11\), 我没有试, 反正大点总没错吧)

因为素数分布随机,所以认为他能填满 \(1 , k\) 之间的线性基,每次插入即可

题解也没有很严谨的证明

否则之间预处理小于 \(\sqrt r\)的质数把这一段质数筛出来插入线性基

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 1e6 + 55;
int prime[maxn], cnt;
int flag[maxn];
bool isprime[maxn];
int n;
vector<ll>v;
ll p[105];
void insert(ll x){
	for(int i = 62; i >= 0; --i)
		if(x & (1ll << i)){
			if(p[i])x ^= p[i];
			else{p[i] = x; break;}
		}	
}
void solve(ll l, ll k){
	if(k >= 16){
		for(int i = 1; i <= k; ++i)insert((1ll << i));
		return;
	}
	ll r = l + (1ll << k) - 1;
	for(int i = 0; i <= r - l; ++i)isprime[i] = 0;
	int mx = sqrt(r);
	for(int i = 1; i <= cnt; ++i){
		if(prime[i] > mx)break;
		for(ll j = ((l + prime[i] - 1) / prime[i]) * prime[i]; j <= r; j += prime[i])
			isprime[j - l] = j != prime[i];
	}
	for(int i = 0; i <= r - l; ++i)if(!isprime[i])v.push_back(i + l);
	for(ll x : v)insert(x);
	v.clear();
}
int main(){
	for(int i = 2; i <= 1e6; ++i){
		if(!flag[i])prime[++cnt] = i;
		for(int j = 1; j <= cnt && i * prime[j] <= 1e6; ++j){
			flag[i * prime[j]] = 1;
			if(i % prime[j] == 0)break;
		}
	}
	scanf("%lld",&n);
	for(int ask = 1; ask <= n; ++ask){
		ll l, r; scanf("%lld%lld",&l, &r);
		++r; 
		int cnt = 0;
		for(int i = 0; i <= 62; ++i)
			if((l & (1ll << i)) && (l + (1ll << i) <= r)){
				solve(l, i);
				l += 1ll << i;
			}
		for(int i = 62; i >= 0; --i)
			if(l + (1ll << i) <= r){
				solve(l, i);
				l += 1ll << i;
			}
		for(int i = 0; i <= 62; ++i)if(p[i])++cnt, p[i] = 0;
		printf("%lld\n",1ll << cnt);
	}
	return 0;
}
posted @ 2022-10-07 17:16  Chen_jr  阅读(103)  评论(1编辑  收藏  举报