题目戳这里: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); return 0; } for(int i = 1; i < N; i++) if(sum[0] - i == - sum[N]) { printf("%d ", 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 ", 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 ", 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 ", 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 ", 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 ", 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 ", 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 ", p); while(~p && q[p].l == L) { R = q[p].r; // printf("p = %d %d ", p, Query(Root, 1, N)); ans[q[p--].p] = Query(Root, 1, N); } // printf("%d ", L); if(p < 0) break; } for(int i = 0; i < Q; i++) printf("%d ", ans[i]); return 0; }
D,E,F都不会写...D题完全没什么思路.E题感觉可以搞但是因为要输出1~N的就不会了,O(N^2)妥妥的TLE啊...
F题推不出来...
这是E题..假如你会的话请评论...
膜11个AK..