OJ 1098
贪心,在遇到检查点的时候保留最大的v个元素即可,用优先队列进行优化。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; inline long long read(){ long long ans = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') f *= (ch == '-') ? -1 : 1, ch = getchar(); do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar(); while(ch >= '0' && ch <= '9'); return ans * f; } const int MAXN = 200001; int a[MAXN], op[MAXN]; priority_queue<int, vector<int>, greater<int> > q; int main(){ int n = read(); for(int i=1; i<=n; i++) op[i] = (getchar() == 'e'), a[i] = read(); for(int i=1; i<=n-1; i++){ if(!op[i]) q.push(a[i]); else while(q.size() >= a[i]) q.pop(); } if(q.size() < a[n]){ printf("-1"); return 0; } int ans = 0; while(!q.empty()) ans += q.top(), q.pop(); printf("%d", ans); return 0; }
OJ 1099
对任意的两条横着的边计算竖着的边的交点,最后取组合,这是$O(n^3)$算法,将一维转为线段树(树状数组)可以达到$O(n^2logn)$
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; inline long long read(){ long long ans = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') f *= (ch == '-') ? -1 : 1, ch = getchar(); do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar(); while(ch >= '0' && ch <= '9'); return ans * f; } const int MAXN = 200001, SIZE = MAXN * 6; struct line{ int posY, posX, end; const bool operator < (const line &x)const{ if(posY != x.posY) return posY < x.posY; return posX < x.posX; } }column[MAXN], row[MAXN]; int cntC = 0, cntR = 0; int hash[MAXN*6]; struct point{ int y, x, val; const bool operator < (const point &a)const{ if(y != a.y) return y > a.y; return x > a.x; } }; priority_queue<point> q; int seg[SIZE + 1]; inline void add(int x,int val){ for(int i=x; i<=SIZE; i+=i&(-i)) seg[i] += val; } inline int ask(int x){ int ans = 0; for(int i=x; i; i-=i&(-i)) ans += seg[i]; return ans; } inline int query(int l,int r){ return ask(r) - ask(l-1); } inline int ord(int val){ return lower_bound(hash + 1, hash + 1 + hash[0], val) - hash; } int main(){ int n = read(); for(int i=1; i<=n; i++){ int y1 = read(), x1 = read(), y2 = read(), x2 = read(); if(x1 > x2) swap(x1, x2); if(y1 > y2) swap(y1, y2); hash[++hash[0]] = x1, hash[++hash[0]] = x2; hash[++hash[0]] = y1, hash[++hash[0]] = y2; if(x1 == x2) column[++cntC] = (line){y1, x1, y2}; else if(y1 == y2) row[++cntR] = (line){y1, x1, x2}; } sort(hash + 1, hash + 1 + hash[0]); hash[0] = unique(hash + 1, hash + 1 + hash[0]) - hash - 1; for(int i=1; i<=cntC; i++) column[i].posX = ord(column[i].posX), column[i].posY = ord(column[i].posY), column[i].end = ord(column[i].end); for(int i=1; i<=cntR; i++) row[i].posX = ord(row[i].posX), row[i].posY = ord(row[i].posY), row[i].end = ord(row[i].end); sort(column + 1, column + 1 + cntC); sort(row + 1, row + 1 + cntR); long long ans = 0; for(int i=1; i<=cntR; i++){ for(int j=1; j<=cntC; j++) if(column[j].posX >= row[i].posX && column[j].posX <= row[i].end && column[j].posY <= row[i].posY && column[j].end >= row[i].posY) q.push((point){column[j].posY, column[j].posX, 1}), q.push((point){column[j].end + 1, column[j].posX, -1}); for(int j=i+1; j<=cntR; j++){ while(!q.empty() && q.top().y <= row[j].posY) add(q.top().x, q.top().val), q.pop(); int cnt = query(row[j].posX, row[j].end); ans += cnt * (cnt - 1) / 2; } while(!q.empty()) add(q.top().x, q.top().val), q.pop(); } printf("%lld", ans); return 0; }
OJ 1100
容斥定理(莫比乌斯反演),将$gcd==d$转化为$d|gcd$之后根据a数组的情况进行组合即可
#include <iostream> #include <cstdio> #include <cstring> using namespace std; inline long long read(){ long long ans = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') f *= (ch == '-') ? -1 : 1, ch = getchar(); do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar(); while(ch >= '0' && ch <= '9'); return ans * f; } const int MAXN = 300001, MOD = 1000000007; int mu[MAXN], prime[MAXN >> 3]; bool isPrime[MAXN]; int num[MAXN], cnt[MAXN], F[MAXN]; int fac[MAXN], inv[MAXN]; void euler(int n){ memset(isPrime, true, sizeof(isPrime)); mu[1] = 1; for(int i=2; i<=n; i++){ if(isPrime[i]){ prime[++prime[0]] = i; mu[i] = -1; } for(int j=1; j<=prime[0]; j++){ if(i * prime[j] > n) break; isPrime[i * prime[j]] = false; if(i % prime[j] == 0){ mu[i * prime[j]] = 0; break; } mu[i * prime[j]] = -mu[i]; } } } inline int pow(int a,int p){ int ans = 1; while(p){ if(p & 1) ans = (long long)ans * a % MOD; a = (long long)a * a % MOD, p >>= 1; } return ans; } inline int C(int n,int m){ return (long long)fac[n] * inv[m] % MOD * inv[n-m] % MOD; } int main(){ int n = read(), m = read(), k = read(); euler(m); for(int i=1; i<=n; i++) num[read()]++; fac[0] = 1; for(int i=1; i<=n; i++) fac[i] = (long long)fac[i-1] * i % MOD; inv[n] = pow(fac[n], MOD-2); for(int i=n-1; i>=0; i--) inv[i] = (long long)inv[i+1] * (i+1) % MOD; for(int i=1; i<=m; i++) for(int j=i; j<=m; j+=i) cnt[i] += num[j]; for(int i=1; i<=m; i++) F[i] = (cnt[i] < n-k) ? 0 : (long long)C(cnt[i], n-k) * pow(m/i-1, cnt[i]-(n-k)) % MOD * pow(m/i, n-cnt[i]) % MOD; for(int i=1; i<=m; i++){ int ans = 0; for(int j=i; j<=m; j+=i) if(mu[j/i]) ans = ((long long)mu[j/i] * F[j] % MOD + ans + MOD) % MOD; printf("%d", ans); if(i != m) putchar(' '); } return 0; }
OJ 1414
两个可持久化树,一个可持久化并查集,一个可持久化线段树,相当于要写两遍代码,但由于第一个可持久化并查集只有最后才有查询,所以可以离线操作,省时间复杂度又省空间复杂度,第二个只能强行线段树了。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <utility> using namespace std; inline long long read(){ long long ans = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') f *= (ch == '-') ? -1 : 1, ch = getchar(); do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar(); while(ch >= '0' && ch <= '9'); return ans * f; } int n, m, size; const int MAXN = 200001; struct Segment{ struct Node{ int sum, son[2]; }node[MAXN << 5]; int head[MAXN], nodeNum; void build(int &x,int l = 1,int r = size){ x = ++nodeNum; if(l == r) return; int mid = (l + r) >> 1; build(node[x].son[0], l, mid); build(node[x].son[1], mid+1, r); } void update(int &x,int pos,int l = 1,int r = size){ node[++nodeNum] = node[x]; x = nodeNum; node[x].sum++; if(l == r) return; int mid = (l + r) >> 1; if(pos <= mid) update(node[x].son[0], pos, l, mid); else update(node[x].son[1], pos, mid+1, r); } int query(int u,int v,int k,int l = 1,int r = size){ if(l == r) return l; int x = node[node[v].son[0]].sum - node[node[u].son[0]].sum, mid = (l + r) >> 1; if(x >= k) return query(node[u].son[0], node[v].son[0], k, l, mid); else return query(node[u].son[1], node[v].son[1], k-x, mid+1, r); } }T; int op[MAXN], a[MAXN], b[MAXN]; int val[MAXN], fa[MAXN]; inline int get(int x){ if(fa[x] == x) return x; return fa[x] = get(fa[x]); } int main(){ n = read(), m = read(); for(int i=1; i<=n; i++) val[i] = read(), fa[i] = i; for(int i=1; i<=m; i++){ op[i] = read(); if(op[i] == 1) a[i] = read(), b[i] = read(); else a[i] = read(); } int p = m; while(p){ if(op[p] == 1){ fa[get(a[p])] = get(b[p]); p--; } else p = a[p]; } int u = read(), q = read(); for(int i=1; i<=n; i++) if(get(u) == get(i)) a[++a[0]] = val[i]; for(int i=1; i<=a[0]; i++) b[i] = a[i]; sort(b+1, b+1+a[0]); size = unique(b+1, b+1+a[0]) - b - 1; for(int i=1; i<=a[0]; i++) a[i] = lower_bound(b+1, b+1+size, a[i]) - b; T.build(T.head[0]); for(int i=1; i<=n; i++){ T.head[i] = T.head[i-1]; T.update(T.head[i], a[i]); } for(int i=1; i<=q; i++){ int l = read(), r = read(), k = read(); printf("%d\n", b[T.query(T.head[l-1], T.head[r], k)]); } return 0; }