CSP-S模拟17
挂分原因: 数组开到 ,读入 ...
A. 最大匹配
问题转化选择 个 剩下选
于是可反悔贪心直接莽
其他人的做法是分情况讨论证明按照 排序,首尾配对
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
大力分讨
每次操作只能增加一种字母的数量
所以整体分两种情况
- 两种字母小于
于是把大于 的那种取出多的部分,改成少的一种,再把多的改成另外一种
- 一种字母小于
假设另外两种 分别比 多 , 那么如果能找到一段恰好出现 的直接一次操作
否则,选择 出现 次, 出现大于 次的,先改成 , 再把多的改回
如果 出现 次, 小于 次就找 出现 次的
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. 三级跳
三元组 可以得到 一定是区间 的最大值,否则可以收缩区间,使得 的选择范围变大
于是考虑将三元组的贡献挂到 上,用线段树进行维护
发现这样我们能够通过查询区间限制 ,但是 难以限制,于是从右侧向左侧扫描线
使用单调栈维护 , 发现这样的关系当 入栈时弹出了 或者 在栈里与 相邻,于是数对的数量级为
在单调栈的同时根据 算出合法的 的范围进行区间修改即可
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. 经典线性基
简单学习了一下线性基
以下做法目前存在问题
将 拆分成若干 的形式
每次处理这样一个区间
如果 (题解说是, 我没有试, 反正大点总没错吧)
因为素数分布随机,所以认为他能填满 之间的线性基,每次插入即可
题解也没有很严谨的证明
否则之间预处理小于 的质数把这一段质数筛出来插入线性基
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】