• 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;
    }
    
  • 相关阅读:
    c++类中比较重要的几个函数
    rosbag使用方法
    2.urllib库的使用
    什么叫做API?
    1.爬虫基础
    正则表达式

    time模块
    random模块
    日志处理
  • 原文地址:https://www.cnblogs.com/silentEAG/p/11731965.html
Copyright © 2020-2023  润新知