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;
}

浙公网安备 33010602011771号