• Codeforces Round #535 (Div. 3) 解题报告


    CF1108A. Two distinct points

    做法:模拟

    如果两者左端点重合就第二条的左端点++就好,然后输出左端点

    #include <bits/stdc++.h>
    using namespace std;
    
    int T;
    int l1, r1, l2, r2;
    
    int main() {
    	scanf("%d", &T);
    	while(T--) {
    		scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
    		printf("%d ", l1);
    		if(l1 == l2) l2++;
    		printf("%d
    ", l2);
    	}
    }
    

    CF1108B. Divisors of Two Integers

    做法:模拟

    坑点好多...首先因为所有因数都有,所以最大的数一定是x,y中的一个.把它拎出来,然后删掉它的因子,剩下的最大的就是x,y中的另外一个了

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int n;
    int a[100000];
    int cnt[101000];
    
    int main() {
    	scanf("%d", &n);
    	int mx = 0;
    	for(int i = 1; i <= n; ++i) {
    		scanf("%d", &a[i]);
    		cnt[a[i]]++;
    		mx = max(mx, a[i]);
    	}
    	printf("%d ", mx);
    	for(int i = 1; i * i <= mx; ++i) {
    		if(mx % i == 0) {
    			cnt[i]--;
    			if(mx / i != i) cnt[mx / i]--;
    		}
    	}
    	int ans = 0;
    	for(int i = 1; i <= mx; ++i) {
    		if(cnt[i] > 0) ans = i;
    	}
    	printf("%d
    ", ans);
    }
    

    CF1108C. Nice Garland

    做法:模拟

    依旧是模拟qwq.显然合法的排列方式也就那么6种...于是6种方式都跑一遍,然后取min即可

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 10;
    
    int n, id = 0;
    char s[N];
    string f[6] = {"RGB", "RBG", "BRG", "BGR", "GRB", "GBR"};
    int main() {
    	scanf("%d%s", &n, s + 1);
    	int ans = N;
    	for(int k = 0; k < 6; ++k) {
    		int sum = 0, pos = 0;
    		for(int i = 1; i <= n; ++i, pos = (pos + 1) % 3) {
    			if(s[i] != f[k][pos]) ++sum;
    		}
    		if(sum < ans) {
    			ans = sum;
    			id = k;
    		}
    	}
    	printf("%d
    ", ans);
    	for(int i = 1, pos = 0; i <= n; ++i, pos = (pos + 1) % 3) putchar(f[id][pos]);
    }
    

    CF1108D. Diverse Garland

    做法:dp

    第一问显然sb dp,设f[i,j]表示点i然后点i填的是RGB中的一个(分别对应一个j),直接转移即可.输出方案反而比较难,有各种方法,我的方法是记录每个dp值的转移点,从后往前推回去.

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 10;
    
    int n, id = 0;
    char s[N], p[3] = {'R', 'G', 'B'};
    int f[N][3], to[N][3];
    char t[N];
    
    int main() {
    	scanf("%d%s", &n, s + 1);
    	memset(f, 0x3f, sizeof(f));
    	f[1][0] = f[1][1] = f[1][2] = 1;
    	if(s[1] == 'R') f[1][0] = 0;
    	else if(s[1] == 'G') f[1][1] = 0;
    	else f[1][2] = 0;
    	for(int i = 2; i <= n; ++i) 
    		for(int j = 0; j < 3; ++j) 
    			for(int k = 0; k < 3; ++k) {
    				if(j == k) continue; 
    				if(p[j] == s[i]) {
    					if(f[i - 1][k] < f[i][j]) 
    						f[i][j] = f[i - 1][k], to[i][j] = k;
    				} else {
    					if(f[i - 1][k] + 1 < f[i][j]) 
    						f[i][j] = f[i - 1][k] + 1, to[i][j] = k;
    				}
    			}
    	int ans = 0, id = 0;
    	if(f[n][0] > f[n][1]) {
    		ans = f[n][1], id = 1;
    	} else ans = f[n][0], id = 0;
    	if(f[n][2] < ans) ans = f[n][2], id = 2;
    	printf("%d
    ", ans);
    	for(int i = n; i; i--) {
    		t[i] = p[id];
    		id = to[i][id];
    	}
    	for(int i = 1; i <= n; ++i) putchar(t[i]); puts("");
    	return 0;
    }
    

    CF1108E1. Array and Segments (Easy version)

    做法:暴力

    考虑怎么样才能让结果最大,把线段分成两类,一种是没有覆盖最大值,一种是有覆盖最大值的,如果选了覆盖最大值的显然只会减小答案,所以可以暴力枚举最大值,并累加不覆盖最大值的线段,对所有情况取max即可复杂度是(O(n^2m))

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 10;
    const int inf = 0x3f3f3f3f;
    
    int n, m, a[N], b[N];
    int vis[N];
    struct seg {
    	int l, r, id;
    } s[N];
    vector<int> Ans;
    int ans = -1;
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 1; i <= m; ++i) scanf("%d%d", &s[i].l, &s[i].r), s[i].id = i;
    	for(int i = 1; i <= n; ++i) {
    		vector<int> cur; cur.clear();
    		int lans = 0;
    		for(int j = 1; j <= n; ++j) b[j] = a[j];
    		for(int j = 1; j <= m; ++j) {
    			if(s[j].l <= i && i <= s[j].r) continue;
    			for(int k = s[j].l; k <= s[j].r; ++k) b[k]--;
    			cur.push_back(j);
    		}
    		for(int j = 1; j <= n; ++j) {
    			if(j == i) continue;
    			lans = max(lans, a[i] - b[j]);
    		}
    		if(lans > ans) {
    			ans = lans;
    			Ans = cur;
    		}
    	}
    	sort(Ans.begin(), Ans.end());
    	printf("%d
    %d
    ", ans, (int)Ans.size());
    	for(int i = 0, len = Ans.size(); i < len; ++i) {
    		printf("%d ", Ans[i]);
    	}
    	return 0;
    } 
    

    CF1108E2. Array and Segments (Hard version)

    做法:线段树优化暴力

    考虑优化E1的暴力,注意到m很小,所以复杂度里面是可以有一个m的.然后利用线段树来优化把线段覆盖下去的过程,即利用线段树维护区间修改,维护区间min.这样的复杂度是(O(nmlogn))的.这样朴素的去做会TLE on 12.发现每次如果加入线段然后再删除线段会有很多重复的操作,可以拿一个数组来标记哪条线段有覆盖哪条没有,如果有线段不能覆盖下去且已经覆盖了,那么就+1回去,能覆盖下去的同理,这样修改次数就大大降低了.这样就能通过本题了

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 10;
    const int inf = 0x3f3f3f3f;
    
    int n, m, a[N], b[N];
    struct seg {
    	int l, r, id;
    } s[N];
    vector<int> Ans;
    int ans = -1;
    
    struct tree {
    	int l, r, mn, tag;
    } t[N<<2];
    
    #define lc (rt << 1)
    #define rc (rt << 1 | 1)
    void pushup(int rt) { t[rt].mn = min(t[lc].mn, t[rc].mn); }
    void build(int l, int r, int rt) {
    	t[rt].l = l; t[rt].r = r; t[rt].mn = inf;
    	if(l == r) { t[rt].mn = a[l]; return; }
    	int mid = (l + r) >> 1;
    	build(l, mid, lc); build(mid + 1, r, rc); pushup(rt);
    }
    void pushdown(int rt) {
    	if(t[rt].tag) {
    		int &x = t[rt].tag;
    		t[lc].tag += x; t[rc].tag += x;
    		t[lc].mn += x; t[rc].mn += x;
    		x = 0;
    	}
    }
    #define l (t[rt].l)
    #define r (t[rt].r)
    void upd(int L, int R, int c, int rt) {
    	if(L <= l && r <= R) {
    		t[rt].tag += c; t[rt].mn += c;
    		return;
    	}
    	pushdown(rt); int mid = (l + r) >> 1;
    	if(L <= mid) upd(L, R, c, lc); 
    	if(R > mid) upd(L, R, c, rc); 
    	pushup(rt);
    }
    #undef l
    #undef r
    #undef lc
    #undef rc
    
    bool vis[N];
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 1; i <= m; ++i) scanf("%d%d", &s[i].l, &s[i].r), s[i].id = i;
    	build(1, n, 1);
    	for(int i = 1; i <= n; ++i) {
    		vector<int> cur; cur.clear();
    		int lans = 0;
    		for(int j = 1; j <= m; ++j) {
    			if(s[j].l <= i && i <= s[j].r) {
    				if(vis[j]) {
    					vis[j] = 0;
    					upd(s[j].l, s[j].r, 1, 1);
    				}
    			} else {
    				if(!vis[j]) vis[j] = 1, upd(s[j].l, s[j].r, -1, 1);
    				cur.push_back(j);
    			}
    		}
    		lans = max(0, a[i] - t[1].mn);
    		if(lans > ans) {
    			ans = lans;
    			Ans = cur;
    		}
    	}
    	sort(Ans.begin(), Ans.end());
    	printf("%d
    %d
    ", ans, (int)Ans.size());
    	for(int i = 0, len = Ans.size(); i < len; ++i) {
    		printf("%d ", Ans[i]);
    	}
    	return 0;
    } 
    

    CF1108F. MST Unification

    做法:最小生成树

    考虑怎么样才会使一个最小生成树不唯一.设有一条边边权为val,且它没有在最小生成树中,那么加入最小生成树之后,就会出来一个环,如果这个环中有一条边边权为val,那么这个mst就是不唯一的.可以倍增实现这个过程但是比较麻烦,也可以选择就是对所有边权相同的边权统一考虑一下:如果此边的两个端点已经在最小生成树里面了,那么显然这条边对mst的唯一性没有影响(因为连接它的边边权一定更小),不用管它.如果两端点之前还未接入最小生成树,但是在加到这条边的时候又已经加入了(即有同样长度的边使两端点联通),那么这条边就一定会对答案产生影响,需要+1保证mst的唯一性

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 200010;
    
    struct edge {
    	int x, y, v;
    } e[N];
    int n, m, f[N];
    
    bool cmp(edge a, edge b) {
    	return a.v < b.v;
    }
    
    int find(int x) {
    	if(f[x] == x) return x;
    	else return f[x] = find(f[x]);
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= m; ++i) {
    		scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].v);
    	}
    	sort(e + 1, e + m + 1, cmp);
    	for(int i = 1; i <= n; ++i) f[i] = i;
    	int ans = 0;
    	for(int i = 1, j = 1; i <= m; i = j) {
    		while(j <= m && e[j].v == e[i].v) ++j;
    		int tot = j - i;
    		for(int k = i; k < j; ++k) {
    			int x = find(e[k].x), y = find(e[k].y);
    			if(x == y) --tot;
    		}
    		for(int k = i; k < j; ++k) {
    			int x = find(e[k].x), y = find(e[k].y);
    			if(x == y) continue;
    			f[x] = y;
    			--tot;
    		}
    		ans += tot;
    	}
    	printf("%d
    ", ans);
    }
    
  • 相关阅读:
    力扣(LeetCode)922. 按奇偶排序数组 II
    力扣(LeetCode)1002. 查找常用字符
    力扣(LeetCode)15. 三数之和
    Java == 和 equals 区别
    力扣(LeetCode)125. 验证回文串
    力扣(LeetCode) 905. 按奇偶排序数组
    力扣(LeetCode)832. 翻转图像
    力扣(LeetCode) 771. 宝石与石头
    Sticks
    荷马史诗
  • 原文地址:https://www.cnblogs.com/henry-1202/p/10356234.html
Copyright © 2020-2023  润新知