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;
}
posted @ 2019-10-24 14:24  SilentEAG  阅读(139)  评论(0编辑  收藏  举报