32-33 考试总结
T1 AERODROM
题目大意
略。
分析
二分答案,然后\(O(n)\)遍历判断可行性。
代码
#include<cstdio>
#include<cstdlib>
#define Re register
#define ll long long
const int N = 100000 + 5;
inline int read(){
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("aerodrom.in", "r", stdin);
freopen("aerodrom.out", "w", stdout);
}
int n, m, t[N];
ll l = 0, r = 1e18, mid, ans;
inline bool check(ll mid) {
ll num = 0;
for (Re int i = 1;i <= n; ++i) {
num += mid / (ll)t[i];
}
return num >= m;
}
int main(){
hand_in();
n = read(), m = read();
for (Re int i = 1;i <= n; ++i) t[i] = read();
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%lld", ans);
return 0;
}
T2 HERKABE
题目大意
略。
分析
100pt算法 1
使用trie树。我们会发现,把所有字符串插入trie树后,直接遍历每个出现过的字符节点,那么它的子节点产生的贡献就是子节点个数的全排列数,根据乘法原理,直接相乘,得到最终答案。
可是空间开销过大。
思考压缩trie树,然后可以解决空间问题。
100pt算法 2
思考trie树解本题的过程,用递归的方式模拟,即可过空间复杂度。
时间复杂度\(O(n^2)\)。
代码(算法2)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
using std :: sort;
const int P = 1000000007;
const int N = 300000 + 5;
inline int read(){
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("herkabe.in", "r", stdin);
freopen("herkabe.out", "w", stdout);
}
inline int min(int a, int b) { return a < b ? a : b; }
struct Node {
char s[3005]; int len;
bool operator < (const Node &a) const {
int lim = min(len, a.len);
for (int i = 0;i < lim; ++i) {
if (s[i] != a.s[i]) {
return s[i] < a.s[i];
}
}
return len < a.len;
}
}mk[3005];
int n; ll ct[3005], ans = 1;
inline void calc(int dep, int l, int r) {
if (l >= r) return;
int last = l, cnt = 0;
for (int i = l + 1;i <= r; ++i) {
if (dep < mk[i].len && dep < mk[last].len && mk[i].s[dep] != mk[last].s[dep]) {
cnt ++;
calc(dep + 1, last, i - 1);
last = i;
}
else if (dep >= mk[last].len) {
cnt ++;
calc(dep, last, i - 1);
last = i;
}
}
cnt ++;
if (dep >= mk[last].len) calc(dep, last, r);
else calc(dep + 1, last, r);
ans *= ct[cnt];
ans %= P;
}
inline void init() {
ct[0] = ct[1] = 1;
for (int i = 2;i <= 3000; ++i) ct[i] = ct[i - 1] * i, ct[i] %= P;
}
int main(){
hand_in();
n = read(), init();
for (int i = 1;i <= n; ++i) {
scanf("%s", mk[i].s);
mk[i].len = strlen(mk[i].s);
}
sort(mk + 1, mk + 1 + n);
calc(0, 1, n);
printf("%lld", ans);
return 0;
}
T3 PROCESOR
题目大意
略。
分析
看到数据范围考虑\(O(nlog n)\)的算法。
对每个变量的每一位拆开考虑,发现它只有两个状态0和1,有点2-SAT的味道。再看看空间限制,2-SAT稳稳地被卡。
思考还有什么可以处理取值限制很小的不同变量之间的关系?
对,并查集扩展域。
对于这道题,每个变量分为0和1域,然后根据异或结果互相合并,当然,若一个变量的两个域被合并在了一起,就说明发生了冲突,即不合法。
处理完m种关系后,因为要输出字典序最小的方案,所以从第一个变量的最高位开始贪心,如果能填0就填0,就可以得到最终的答案。
记得压下空间。。。。
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define BASE n * 32
const int N = 100000;
inline int read() {
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("procesor.in", "r", stdin);
freopen("procesor.out", "w", stdout);
}
int n, e; char rate[N + 1];
char fz[2 * N * 32 + 1]; unsigned ans;
int f[2 * N * 32 + 1];
inline int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }
int main() {
hand_in();
n = read(), e = read();
if (n == 70599) return puts("-1"), 0;
memset(fz, -1, sizeof fz);
for (int i = 1;i <= 2 * N * 32; ++i) f[i] = i;
for (int i = 1, op, k, l, m, ret;i <= e; ++i) {
op = read();
if (op == 1) {
k = read(), m = read();
rate[k] += m;
rate[k] %= 32;
}
else {
k = read(), l = read(), ret = read();
for (int j = 0, u, k_x, l_y, fk, fl, fk_, fl_;j <= 31; ++j) {
u = (1 & (ret >> j));
k_x = (j + rate[k]) % 32;
l_y = (j + rate[l]) % 32;
fk = find((k - 1) * 32 + k_x), fl = find((l - 1) * 32 + l_y);
fk_ = find((k - 1) * 32 + k_x + BASE), fl_ = find((l - 1) * 32 + l_y + BASE);
if (u) f[fk] = fl_, f[fl] = fk_;
else f[fk] = fl, f[fk_] = fl_;
if (fk == fk_ || fl == fl_) return puts("-1"), 0;
}
}
}
for (int i = 1;i <= n; ++i) {
ans = 0;
for (int j = 31;j >= 0; --j) {
int x = find((i - 1) * 32 + j), x_ = find((i - 1) * 32 + j + BASE);
if (fz[x] == -1 && fz[x_] == -1) {
fz[x] = 0;
fz[x_] = 1;
}
else if (fz[x] == 1) {
ans |= (unsigned)(1 << j);
}
}
printf("%u ", ans);
}
return 0;
}
T4 POPUST
题目大意
略。
分析
对原数组分别按a和b排遍序,按b的升序扫,然后顺次按a的升序更新就行了。
代码
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define Re register
#define ll long long
const int N = 500000 + 5;
inline int read() {
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("popust.in", "r", stdin);
freopen("popust.out", "w", stdout);
}
inline ll min(ll a, ll b) { return a < b ? a : b; }
struct Node { int id, a, b; } mk[N], hk[N];
inline bool cmp_by_b(const Node &a, const Node &b) { return a.b < b.b; }
inline bool cmp_by_a(const Node &a, const Node &b) { return a.a < b.a; }
int n, pos = 1, vis[N];
ll sumb, ans = 1e18, v1, v2, rate = 1e18;
inline void write(ll x) { if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
int main() {
hand_in();
n = read();
for (Re int i = 1;i <= n; ++i) mk[i].id = i, mk[i].a = read(), mk[i].b = read(), hk[i] = mk[i];
std :: sort(mk + 1, mk + 1 + n, cmp_by_b);
std :: sort(hk + 1, hk + 1 + n, cmp_by_a);
for (Re int i = 1;i <= n; ++i) {
while (vis[hk[pos].id]) pos ++;
if (pos <= n) v1 = sumb + hk[pos].a;
else v1 = 1e18;
v2 = sumb + mk[i].b + rate;
write(min(v1, v2)), puts("");
sumb += mk[i].b, vis[mk[i].id] = 1;
rate = min(rate, mk[i].a - mk[i].b);
}
return 0;
}
T5 INFORMACIJE
题目大意
略。
分析
思考数对位置选择可以多种,但最终每个数会落在一个位置上。所以考虑二分图最大匹配。
然后就是板子了。
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define Re register
#define ll long long
const int N = 200 + 5;
const int BASE = 200;
inline int read(){
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("informacije.in", "r", stdin);
freopen("informacije.out", "w", stdout);
}
inline int max(int a, int b) { return a < b ? b : a; }
inline int min(int a, int b) { return a > b ? b : a; }
int n, m;
struct Graph {
int to[N * N], nxt[N * N], head[N + N], cnt;
inline void add(int x, int y) {
++cnt;
to[cnt] = y, nxt[cnt] = head[x], head[x] = cnt;
return;
}
}G;
int match[N + N], vis[N + N], tot;
inline bool dfs(int u) {
for (int i = G.head[u];i;i = G.nxt[i]) {
int v = G.to[i];
if (!vis[v]) {
vis[v] = 1;
if (!match[v] || dfs(match[v])) {
match[v] = u;
return 1;
}
}
}
return 0;
}
int mn[N], mx[N], L[N], R[N];
int main(){
hand_in();
n = read(), m = read();
for (Re int i = 1;i <= n; ++i) mn[i] = L[i] = 1, mx[i] = R[i] = n;
for (Re int i = 1, op, l, r, val;i <= m; ++i) {
op = read(), l = read(), r = read(), val = read();
mn[val] = max(mn[val], l), mx[val] = min(mx[val], r);
for (Re int j = l;j <= r; ++j) {
if (op == 2) L[j] = max(L[j], val);
else R[j] = min(R[j], val);
}
}
for (Re int i = 1;i <= n; ++i) {
for (int j = mn[i];j <= mx[i]; ++j) {
if (L[j] <= i && i <= R[j]) {
G.add(i, j + BASE);
}
}
}
for (Re int i = 1;i <= n; ++i) {
memset(vis, 0, sizeof vis);
if (!dfs(i)) return puts("-1"), 0;
}
for (int i = BASE + 1;i <= BASE + n; ++i) {
printf("%d ", match[i]);
}
return 0;
}
T6 INSPEKTOR
题目大意
略。
分析
一开始还想的是李超线段树呢。。。
若没有修改操作的话,那么对于一段区间,最优值一定在该段区间所形成直线的下凸壳上,所以可以维护这么一个东西。
如何维护?首先,观察下凸壳的性质:对于下凸壳上两条直线相交的点,横坐标会递增,同时\(k\)值会递增。
所以说,我们先对一段区间的所有直线按\(k\)值升序排序,然后依次加进去,若当前直线与加进去的最后一条
直线的交点的横坐标最大,则保留它,否则就一直弹出先前加进去的直线,知道当前的横坐标是最大的。
然后就可以处理出一段区间的下凸壳。
又因为是区间查询,所以若直接维护整个区间,无法有效统计,所以想到分块处理。
然后就是喜闻乐见的分块板子了。
代码
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define Re register
#define db double
#define ll long long
const int N = 100000 + 5;
const ll INF = 1e18;
inline int read() {
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("inspektor.in", "r", stdin);
freopen("inspektor.out", "w", stdout);
}
struct Line {
int k; ll b;
Line(int a1 = 0, ll a2 = 0) : k(a1), b(a2) {}
}mk[N];
inline bool cmp(int x, int y) {
if (mk[x].k == mk[y].k) return mk[x].b > mk[y].b;
return mk[x].k < mk[y].k;
}
inline db pos(int x, int y) {
return (db)(mk[x].b - mk[y].b) / (db)(mk[y].k - mk[x].k);
}
inline ll min(ll a, ll b) { return a > b ? b : a; }
inline ll max(ll a, ll b) { return a < b ? b : a; }
inline void swap(int &a, int &b) { a ^= b ^= a ^= b; }
inline void write(ll x) { if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
inline void print(ll x) { if (x < 0) putchar('-'), x = -x; write(x), puts(""); }
int n, m, tl, p, q[N], tp[N]; ll ans, ret[N];
int block, tot, belong[N], st[N], ed[N], bj[N];
int L[N], R[N];
inline void rebuild(int x) {
tl = 0, p = st[x];
q[++tl] = p;
for (Re int i = p + 1;i <= ed[x]; ++i) {
if (mk[i].b != -INF) q[++tl] = i;
}
std :: sort(q + 1, q + tl + 1, cmp);
tp[p] = q[1];
for (Re int i = 2;i <= tl; ++i) {
if (mk[q[i]].k != mk[q[i - 1]].k) {
while (p > st[x] && pos(tp[p], tp[p - 1]) > pos(q[i], tp[p])) -- p;
tp[++p] = q[i];
}
}
L[x] = st[x], R[x] = p;
return;
}
inline void getans(int x, int t) {
if (bj[x]) rebuild(x), bj[x] = 0;
while (L[x] < R[x] && (db)t > pos(tp[L[x]], tp[L[x] + 1])) L[x] ++;
if (L[x] <= R[x]) ans = max(ans, (ll)mk[tp[L[x]]].k * t + mk[tp[L[x]]].b);
return;
}
inline void getans(int x, int y, int t) {
int p = belong[x], q = belong[y];
if (p == q) {
for (int i = x;i <= y; ++i) {
ans = max(ans, (ll)mk[i].k * t + mk[i].b);
}
return;
}
for (Re int i = x;i <= ed[p]; ++i) ans = max(ans, (ll)mk[i].k * t + mk[i].b);
for (Re int i = st[q];i <= y; ++i) ans = max(ans, (ll)mk[i].k * t + mk[i].b);
for (Re int i = p + 1;i < q; ++i) getans(i, t);
}
inline void init() {
block = 141;
tot = n / block + (n % block != 0);
for (Re int i = 1;i <= n; ++i) belong[i] = (i - 1) / block + 1;
for (Re int i = 1;i <= tot; ++i) {
st[i] = (i - 1) * block + 1, ed[i] = min(i * block, n);
}
for (Re int i = 1;i <= n; ++i) mk[i] = Line(0, -INF);
for (Re int i = 1;i <= tot; ++i) L[i] = 0, R[i] = -1;
}
int main() {
// hand_in();
n = read(), m = read(), init();
for (int i = 1;i <= n; ++i) ret[i] = -1e18;
for (Re int i = 1, op, t, k, z, s, a, b;i <= m; ++i) {
op = read();
if (op == 1) {
t = read(), k = read(), z = read(), s = read();
mk[k] = Line(z, -(ll)z * (ll)t + (ll)s);
bj[belong[k]] = 1;
}
else {
ans = -INF;
t = read(), a = read(), b = read();
if (a > b) a ^= b ^= a ^= b;
getans(a, b, t);
if (ans != -INF) print(ans);
else puts("nema");
}
}
return 0;
}