Codeforces Round #673 (Div. 1)题解

第一次做div1的Virtual participation,只写出来AB,自闭了


A. k-Amazing Numbers

题解:对于每个数求出最大的间隔,从小到大排序,更新答案即可

写个桶也可以做到$O(n)$,但是能过就不管了

时间复杂度$O(n \log n)$,空间复杂度$O(n)$

#include <bits/stdc++.h>
using namespace std;
int n, a[300011], las[300011], w, ans;
struct D {
	int maxn, id;
} b[300011];
bool cmp(D p1, D p2) {
	return p1.maxn < p2.maxn;
}
void solve() {
	memset(las, 0, sizeof(las));
	memset(b, 0, sizeof(b));
	scanf("%d", &n); ans = n+1;
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for(int i = 1; i <= n; i++) {
		b[a[i]].maxn = max(b[a[i]].maxn, i - las[a[i]]);
		las[a[i]] = i;
	}
	for(int i = 1; i <= n; i++) 
		b[i].maxn = max(b[i].maxn, n + 1 - las[i]), b[i].id = i;
	sort(b+1, b+n+1, cmp); w = 0;
	for(int i = 1; i <= n; i++) {
		while(b[w+1].maxn <= i && w < n) ++ w, ans = min(ans, b[w].id);
		if(ans == n+1) printf("-1 ");
		else printf("%d ", ans);
	}
	printf("\n");
}
int main() {
	int T; scanf("%d", &T);
	while(T--) solve();
	return 0;
}

 


B. Make Them Equal

题解:注意到$a[i]$减的数与$a[j]$加的数相同,所以序列之和不变.

取平均值,若不为整数,则输出-1.

因为$i=1$可以最细化每个数的变化,故考虑所有操作围绕$a[1]$进行.

有一种方案为(以下$i∈[2,n]$):

1.将$a[i]$变为$i$的倍数.

2.将$a[i]$变为0.

对于每个i重复操作1,2(这两个操作可以使$a[1] \gets a[1]+a[i],a[i] \gets 0$).

3.将$a[1]$的值平均分给每个$a[i]$.

这样操作次数为$3(n-1)$,符合要求.

考虑正确性:

对于1操作,因为$a[i] \geqslant 1$,所以在处理到第$i$个数时,$a[1]=sum[1,i-1] \geqslant i-1$.

又因为$a[i] \bmod i \leqslant i-1$,所以将$a[i]$变为$i$的倍数时,$a[1]$一定非负.

其余操作的显然不会使序列中出现负数.

时间复杂度$O(n)$,空间复杂度$O(n)$

#include <bits/stdc++.h>
using namespace std;
int a[100011];
int n, sum;
void solve() {
	scanf("%d", &n); sum = 0; 
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		sum += a[i];
	}
	if(sum % n) {
		printf("-1\n");
		return;
	}
	sum /= n;
	cout << 3 * (n-1) << endl;
	for(int i = 2; i <= n; i++) {
		int x = a[i] % i;
		cout << 1 << " " << i << " " << (i - x) % i << endl;
		a[1] -= (i - x) % i; a[i] += (i - x) % i; 
		cout << i << " " << 1 << " " << a[i] / i << endl;
		a[1] += a[i]; a[i] = 0;  
	}
	for(int i = 2; i <= n; i++) {
		cout << 1 << " " << i << " " << sum << endl;
		a[1] -= sum; a[i] += sum; 
	}
}
int main() {
	int T;
	scanf("%d", &T);
	while(T--) solve();
	return 0;
}

 


 C. XOR Inverse

题解:先把所有数转化为二进制(以下“位”均指二进制位).

从高到低一位一位地考虑,在当前第i位,统计前面位全部相等且第i位不等的数对和逆序对个数

若逆序对个数大于正序对个数,那么x的第i位应为1,否则为0

然后就不会了看了题解发现01trie这个好东西,可以$O(n \log V)$求上面的两个量

具体求法($siz[i]$为以$i$为根的子树内数字个数):

逆序对即在插入时若当前位为0,则当前节点逆序对数$+=siz[son[1]]$

数对即$siz[son[0]]*siz[son[1]]$

注意开long long统计答案

时间复杂度$O(n \log V)$,空间复杂度$O(n \log V)$

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5;
struct node {
	int son[2];
	ll siz, inv, num;
} a[N*30+11];
int tot, n, b[31], cnt, s, rt;
ll ans, x;
ll sum[31], Inv[31];
void ins(int &k, int w) {
	if(!k) k = ++tot;
	if(w < 0) {
		a[k].siz ++;
		return;
	}
	ins(a[k].son[b[w]], w-1);
	if(b[w] == 0) a[k].inv += a[a[k].son[1]].siz;
	a[k].siz = a[a[k].son[0]].siz + a[a[k].son[1]].siz;
	a[k].num = a[a[k].son[0]].siz * a[a[k].son[1]].siz;
}
void dfs(int k, int w) {
	if(!k || w < 0) return;
	sum[w] += a[k].num;
	Inv[w] += a[k].inv;
	dfs(a[k].son[0], w-1);
	dfs(a[k].son[1], w-1);
}
int main() {
	scanf("%d", &n); rt = 1; tot = 1;
	for(int i = 1; i <= n; i++) {
		scanf("%d", &s);
		cnt = 0; memset(b, 0, sizeof(b));
		while(s) {
			b[cnt++] = s % 2;
			s /= 2;
		}
		ins(rt, 30);
	}
	dfs(rt, 30); 
	for(int i = 30; i >= 0; i--) {
		if(Inv[i] > sum[i] - Inv[i]) x += (1 << i), ans += sum[i] - Inv[i];
		else ans += Inv[i];
	}
	cout << ans << " " << x << endl;
	return 0;
}

 


D. Graph and Queries

题解:官方正解是kruskal重构树,可是我不会

于是参考了$\texttt{Fee_cle6418}$的题解,写了个启发式分裂

把删边反过来跑做加边操作,预处理出删除的边中哪些是树边和分裂的两部分的$siz$

对于每次删边操作,如果删除的是树边,则把$siz$较小的一部分分裂出来

可以对于每个联通块用堆维护,时间复杂度是$O(n \log^2 n)$

考虑优化掉堆的$\log n$复杂度,我们可以用vector存,初始化时对vector排序

新建联通块时只需要设置一个$belong$数组,存储每个值现在在哪一个联通块中即可

时间复杂度$O(n \log n)$

#include <bits/stdc++.h>
using namespace std;
int n, m, q, ex[300011], ey[300011];
struct oper {
	int opt, s, sma, big;
	bool tree;
} a[500011];
int p[200011], siz[200011], fa[200011], belong[200011];
vector <int> h[200011]; //注意:因为p[i]为1~n的一个排列,所以vector中存储的是数,不是下标 
bool del[300011], used[200011];
int find(int w) {
	if(fa[w] == w) return w;
	return find(fa[w]);
}
int query(int w) {
	while(h[w].size() && (belong[h[w].back()] != w || used[h[w].back()])) h[w].pop_back();
	if(!h[w].size()) return 0;
	int ret = h[w].back();
	h[w].pop_back(); used[ret] = 1;
	return ret;
}
int main() {
	scanf("%d%d%d", &n, &m, &q);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &p[i]); fa[i] = i;
		h[i].push_back(p[i]); siz[i] = 1;
	}
	for(int i = 1; i <= m; i++) 
		scanf("%d%d", &ex[i], &ey[i]);
	for(int i = 1; i <= q; i++) {
		scanf("%d%d", &a[i].opt, &a[i].s);
		if(a[i].opt == 2) del[a[i].s] = 1;
		a[i].tree = 0;
	}
	for(int i = 1; i <= m; i++) {
		if(del[i]) continue;
		int fx = find(ex[i]), fy = find(ey[i]);
		if(fx == fy) continue;
		if(siz[fx] < siz[fy]) swap(fx, fy);
		siz[fx] += siz[fy]; fa[fy] = fx;
		for(int j = 0; j < (int)h[fy].size(); j++)
			h[fx].push_back(h[fy][j]); 
	}
	for(int i = q; i >= 1; i--) {
		if(a[i].opt == 2) {
			int fx = find(ex[a[i].s]), fy = find(ey[a[i].s]);
			if(fx == fy) continue;
			if(siz[fx] < siz[fy]) swap(fx, fy);
			a[i].big = fx; a[i].sma = fy;
			fa[fy] = fx; siz[fx] += siz[fy];
			for(int j = 0; j < (int)h[fy].size(); j++)
				h[fx].push_back(h[fy][j]); 
			a[i].tree = 1;
		}
	}
	for(int i = 1; i <= n; i++) {
		sort(h[i].begin(), h[i].end());
		belong[p[i]] = find(i);
	}
	for(int i = 1; i <= q; i++) {
		if(a[i].opt == 1) printf("%d\n", query(find(a[i].s))); 
		else if(a[i].tree) {
			for(int j = 0; j < (int)h[a[i].sma].size(); j++) 
				belong[h[a[i].sma][j]] = a[i].sma;
			fa[a[i].sma] = a[i].sma;
		}
	}
	return 0;
}

 

posted @ 2020-11-12 21:30  huangxuanao  阅读(144)  评论(0)    收藏  举报