CSP-S模拟17

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

A. 最大匹配

问题转化选择 nmax(ai,bi) 剩下选min(ai,bi)

于是可反悔贪心直接莽

其他人的做法是分情况讨论证明按照 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 分别比 na,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) 可以得到 wa,wb 一定是区间 [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×2k,(x+1)×2k) 的形式

每次处理这样一个区间

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

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

题解也没有很严谨的证明

否则之间预处理小于 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 @   Chen_jr  阅读(111)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示