51NOD 算法马拉松8

时间:2022-08-06 18:50:38

题目戳这里:51NOD算法马拉松8

51NOD 算法马拉松8

某天晚上kpm在玩OSU!之余让我看一下B题...然后我就被坑进了51Nod...

A.还是01串

水题..怎么乱写应该都可以。记个前缀和然后枚举就行了.时间复杂度O(N)

#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std; const int maxn = 1000009; char s[maxn];
int sum[maxn], N; int main() {
// freopen("test.in", "r", stdin); memset(sum, 0, sizeof sum); scanf("%s", s);
N = strlen(s);
sum[N] = 0;
for(int i = N; i--; )
sum[i] = sum[i + 1] + (s[i] == '1');
if(!sum[0]) {
puts("0"); return 0;
}
if(sum[0] == N) {
printf("%d\n", N); return 0;
}
for(int i = 1; i < N; i++) if(sum[0] - i == - sum[N]) {
printf("%d\n", i); return 0;
}
puts("-1"); return 0;
}

B.差和问题

题目大意:维护一个集合S,支持加入/删除元素v, 询问S里面的元素两两之差绝对值之和.N,Q≤100000.

拆掉绝对值, 每次我们加入或者删除x时,用平衡树维护x对答案的贡献即可,时间复杂度O(NlogN)

(PS.官方题解好像是离散化+树状数组..常数应该小一点..我一开始没加读入优化还被卡TLE了2个点..)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<queue> using namespace std; typedef long long ll; const int maxn = 200009; int read() {
char c = getchar();
int ret = 0;
for(; !isdigit(c); c = getchar());
for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
return ret;
} struct Node { Node* ch[2];
ll sum;
int r, v, s; void upd() {
s = ch[0]->s + ch[1]->s + 1;
sum = ch[0]->sum + ch[1]->sum + v;
} } pool[maxn], *Null, *Root;
queue<Node*> q; Node* newNode(int v) {
Node* t = q.front(); q.pop();
t->s = 1;
t->v = v;
t->r = rand();
t->ch[0] = t->ch[1] = Null;
return t;
} void InitTreap() {
for(int i = 1; i < maxn; i++)
q.push(pool + i);
Root = Null = pool;
Null->sum = 0;
Null->s = 0;
Null->ch[0] = Null->ch[1] = Null;
} void Rotate(Node*&t, int d) {
Node* o = t->ch[d ^ 1];
t->ch[d ^ 1] = o->ch[d];
o->ch[d] = t;
t->upd(); o->upd();
t = o;
} void Insert(Node*&t, int v) {
// printf("%d\n", v);
if(t == Null) {
t = newNode(v);
} else {
int d = (v > t->v);
Insert(t->ch[d], v);
if(t->ch[d]->r > t->r)
Rotate(t, d ^ 1);
}
t->upd();
} void Delete(Node*&t, int v) {
int d = (t->v == v ? -1 : (v > t->v));
if(!~d) {
if(t->ch[0] != Null && t->ch[1] != Null) {
int _d = (t->ch[0]->r > t->ch[1]->r);
Rotate(t, _d);
Delete(t->ch[_d], v);
} else {
q.push(t);
t = (t->ch[0] == Null ? t->ch[1] : t->ch[0]);
}
} else
Delete(t->ch[d], v);
if(t != Null)
t->upd();
} ll ans = 0, sum = 0;
int sz = 0, N, Q; void update(int v, bool typ) {
int _sz = 0;
ll _sum = 0;
for(Node* t = Root; t != Null; ) {
if(t->v <= v)
_sz += t->ch[0]->s + 1, _sum += t->ch[0]->sum + t->v, t = t->ch[1];
else
t = t->ch[0];
} if(typ) {
// printf("%d %d %d %lld %lld\n", v, sz, _sz, sum, _sum);
ans += ll(v) * _sz - _sum;
ans += sum - _sum - ll(v) * (sz - _sz);
sz++, sum += v;
} else {
ans -= ll(_sz) * v - _sum;
ans -= sum - _sum - ll(v) * (sz - _sz);
sz--, sum -= v;
}
} bool Find(Node* t, int v) {
if(t == Null) return false;
if(t->v == v) return true;
return v < t->v ? Find(t->ch[0], v) : Find(t->ch[1], v);
} int main() {
// freopen("test.in", "r", stdin); InitTreap(); N = read(); Q = read();
for(int i = 0; i < N; i++) {
int v = read();
update(v, 1);
Insert(Root, v);
}
while(Q--) {
int opt = read();
if(opt == 3)
printf("%lld\n", ans);
else {
int v = read();
if(opt == 1) {
update(v, 1);
Insert(Root, v);
} else {
if(!Find(Root, v)) {
puts("-1"); continue;
}
update(v, 0);
Delete(Root, v);
}
}
} return 0;
}

C.找朋友

题目大意:给两个长度为N的数列A、B,一个有m个元素的集合K.Q个询问[l,r]内满足|Bi-Bj|∈K 的最大Ai+Aj.N,Q≤100000,M≤10

一开始不太会做...用莫队的话是O(M*N^1.5)..会TLE..因为至多有M*N对满足题意的数对,我们可以直接考虑他们对答案的贡献...那么离线将询问排序然后线段树维护就可以了,时间复杂度O(MNlogN)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype> using namespace std; const int maxn = 100009; int read() {
char c = getchar();
int ret = 0;
for(; !isdigit(c); c = getchar());
for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
return ret;
} int N, Q, M, A[maxn], B[maxn], _B[maxn], K[maxn], ans[maxn]; struct QUERY { int l, r, p; void Read(int x) {
l = read(); r = read(); p = x;
} bool operator < (const QUERY &o) const {
return l < o.l;
} } q[maxn]; void Init() {
memset(_B, -1, sizeof _B);
N = read(); Q = read(); M = read();
for(int i = 1; i <= N; i++) A[i] = read();
for(int i = 1; i <= N; i++) _B[B[i] = read()] = i;
for(int i = 0; i < M; i++) K[i] = read();
for(int i = 0; i < Q; i++) q[i].Read(i);
sort(q, q + Q);
// for(int i = 0; i < Q; i++)
// printf("[ %d , %d ] %d\n", q[i].l, q[i].r, q[i].p);
} int L, R, V; struct Node { Node *l, *r;
int v; Node() : v(0) {
l = r = NULL;
} void upd() {
if(l)
v = max(l->v, r->v);
} } pool[maxn << 1], *pt = pool, *Root; void Build(Node* t, int l, int r) {
if(l == r)
return;
int m = (l + r) >> 1;
Build(t->l = pt++, l, m);
Build(t->r = pt++, m + 1, r);
} void Modify(Node* t, int l, int r) {
if(l == r)
t->v = max(t->v, V);
else {
int m = (l + r) >> 1;
R <= m ? Modify(t->l, l, m) : Modify(t->r, m + 1, r);
t->upd();
}
} int Query(Node* t,int l, int r) {
if(L <= l && r <= R)
return t->v;
int m = (l + r) >> 1;
return max(L <= m ? Query(t->l, l, m) : 0, m < R ? Query(t->r, m + 1, r) : 0);
} int main() {
// freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout); Init(); Build(Root = pt++, 1, N); int p = Q - 1;
for(L = N; L; L--) {
// printf("%d\n", L);
int &v = B[L];
for(int j = 0; j < M; j++) {
if(v - K[j] >= 1 && ~_B[v - K[j]] && _B[v - K[j]] > L) {
V = A[L] + A[R = _B[v - K[j]]];
// printf("%d\n", V);
Modify(Root, 1, N);
}
if(v + K[j] <= N && ~_B[v + K[j]] && _B[v + K[j]] > L) {
V = A[L] + A[R = _B[v + K[j]]];
Modify(Root, 1, N);
}
}
// printf("%d\n", p);
while(~p && q[p].l == L) {
R = q[p].r;
// printf("p = %d %d \n", p, Query(Root, 1, N));
ans[q[p--].p] = Query(Root, 1, N);
}
// printf("%d\n", L);
if(p < 0) break;
} for(int i = 0; i < Q; i++)
printf("%d\n", ans[i]); return 0;
}

D,E,F都不会写...D题完全没什么思路.E题感觉可以搞但是因为要输出1~N的就不会了,O(N^2)妥妥的TLE啊...

F题推不出来...

这是E题..假如你会的话请评论...

51NOD 算法马拉松8

膜11个AK..