Codeforces Round #429(Div 1)
A. Leha and Function
大家都一眼了结论,只有我个sx真的去证明了…
猜想:逆序和大于等于乱序和大于等于顺序和。我们只需要证明将 B 数组排序后,如果存在一对相邻的逆序对,交换后结果不会更劣,再根据冒泡排序算法的正确性即可得证。也即:
也就是证明:
也就是证:
由于 k1≤k2 ,上式显然成立。所以结论就是:逆序和最大。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int A[MAXN], B[MAXN], Ap[MAXN], n;
priority_queue<pair<int, int> > pr;
priority_queue<pair<int, int>, vector<pair<int,int> >, greater<pair<int, int> > > pr2;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &A[i]), pr2.push(make_pair(A[i], i));
for (int i = 1; i <= n; i++) scanf("%d", &B[i]), pr.push(make_pair(B[i], i));
for (int i = 1; i <= n; i++) {
pair<int, int> a = pr2.top(), b = pr.top();
pr.pop(), pr2.pop();
Ap[b.second] = a.first;
}
for (int i = 1; i <= n; i++)
printf("%d ", Ap[i]);
puts("");
return 0;
}
B. Leha and another game about graph(补)
考虑增量法构造,每一次选取两个 di=1 或者一个 di=1 一个 dj=−1 ,用一条路径连接,路径上的边状态翻转。
正确性在于,如果经过一段已经经过的路径,可以删去这段路径,并将上下两部分视为两个路径,不影响结果。
然后发现任意一个生成森林和整张图是等价的。
然后就可以喜闻乐见的NOIP经典方法树上差分了。由于异或运算的性质,甚至不需要求lca。(当然你也可以选择树剖233)。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
int fa[MAXN];
inline int findf(int i)
{ return fa[i]?fa[i]=findf(fa[i]):i; }
struct node {
int to, next, id;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j, int id)
{ edge[++top] = (node) {j, head[i], id}, head[i] = top; }
int n, m;
int d[MAXN];
vector<int> vec[MAXN], v[MAXN];
int depth[MAXN], pre[MAXN];
int tag[MAXN], del[MAXN], cnt = 0;
void dfs_calc(int nd, int f)
{
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f) continue;
pre[to] = edge[i].id, dfs_calc(to, nd), tag[nd] ^= tag[to];
}
if (tag[nd] == 1) del[pre[nd]] = 1;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
for (int i = 1; i <= m; i++) {
int u, v; scanf("%d%d", &u, &v);
if (findf(u) != findf(v))
fa[findf(u)] = findf(v), push(u, v, i), push(v, u, i);
}
for (int i = 1; i <= n; i++) {
if (d[i] == 1)
vec[findf(i)].push_back(i);
if (d[i] == -1)
v[findf(i)].push_back(i);
}
for (int i = 1; i <= n; i++)
if (fa[i] == 0) {
if ((vec[i].size()&1) && v[i].empty()) { puts("-1"); return 0; }
int k = vec[i].size()/2;
for (int j = 0; j < k; j++) {
int a = vec[i][j], b = vec[i][j+k];
tag[a] ^= 1, tag[b] ^= 1;
}
if (vec[i].size()&1){
int a = vec[i][vec[i].size()-1], b = v[i][0];
tag[a] ^= 1, tag[b] ^= 1;
}
dfs_calc(i, 0);
}
for (int i = 1; i <= n; i++)
if (del[i] == 1)
cnt++;
printf("%d\n", cnt);
for (int i = 1; i <= n; i++)
if (del[i] == 1)
printf("%d ", i);
puts("");
return 0;
}
D. Destiny(补)
首先用莫队+set+线段树/树状数组很容易得到 O(nn−−√lgn) 的算法,但是并不能过…
O(nn−−√lgn) (TLE)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005, N = 1<<19;
int n, q;
int a[MAXN];
int T[MAXN], ans[MAXN];
int bk;
struct query {
int x, y, k, id;
friend bool operator < (const query &a, const query &b)
{ return a.x/bk == b.x/bk ? a.y < b.y : a.x < b.x; }
} qy[MAXN];
multiset<int> hp[MAXN];
int zkw[N+N+1];
inline void modify(int pos, int dt)
{
if (pos == 0) return;
// cerr << "M" << pos << " " << dt << endl;
pos += N-1;
zkw[pos] = dt;
for (pos >>= 1; pos; pos >>= 1)
zkw[pos] = min(zkw[pos<<1], zkw[pos<<1|1]);
}
inline int get_ans(int L, int R)
{
int ans = INT_MAX;
// cerr << L << " " << R << endl;
for (L += N-1, R += N-1; L <= R; L >>= 1, R >>= 1) {
if (L&1) ans = min(ans, zkw[L++]);
if (!(R&1)) ans = min(ans, zkw[R--]);
}
// cerr << "Q" << L << " " << R << " " << ans << endl;
return ans == INT_MAX ? -1 : ans;
}
inline void pop(int dt)
{
if (T[dt]) {
hp[T[dt]].erase(dt);
if (!hp[T[dt]].empty()) modify(T[dt], *hp[T[dt]].begin());
else modify(T[dt], INT_MAX);
}
hp[--T[dt]].insert(dt);
modify(T[dt], *hp[T[dt]].begin());
}
inline void push(int dt)
{
if (T[dt]) {
hp[T[dt]].erase(dt);
if (!hp[T[dt]].empty()) modify(T[dt], *hp[T[dt]].begin());
else modify(T[dt], INT_MAX);
}
hp[++T[dt]].insert(dt);
modify(T[dt], *hp[T[dt]].begin());
}
int main()
{
scanf("%d%d", &n, &q);
bk = int(sqrt(n)+0.5);
if (!bk) bk = 1;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= N+N-1; i++) zkw[i] = INT_MAX;
for (int i = 1; i <= q; i++) scanf("%d%d%d", &qy[i].x, &qy[i].y, &qy[i].k), qy[i].id = i;
sort(qy+1, qy+q+1);
int L = 1, R = 0;
for (int i = 1; i <= q; i++) {
while (L < qy[i].x) pop(a[L]), L++;
while (L > qy[i].x) L--, push(a[L]);
while (R < qy[i].y) R++, push(a[R]);
while (R > qy[i].y) pop(a[R]), R--;
int Lb = (qy[i].y-qy[i].x+1)/qy[i].k+1;
ans[qy[i].id] = get_ans(Lb, n);
}
for (int i = 1; i <= q; i++)
printf("%d\n", ans[i]);
return 0;
}
考虑分块。设块大小为
b
,如果序列长度小于
等号成立当且仅当 b=5nb 也就是 b=5n−−√ 。复杂度很不科学但跑得比谁都快…最慢数据只要592ms…
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
int n, q;
int a[MAXN], bk, bk2;
struct query {
int x, y, k, id;
friend bool operator < (const query &a, const query &b)
{ return a.x/bk2 == b.x/bk2 ? a.y < b.y : a.x < b.x; }
} qy[MAXN];
int T[MAXN];
int g[MAXN], top = 0;
int p[MAXN];
int main()
{
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), T[a[i]]++;
bk = int(sqrt(5*n)+0.5);
if (bk > n) bk = n;
if (bk < 1) bk = 1;
bk2 = int(sqrt(5*n)+0.5);
if (bk2 > n) bk2 = n;
if (bk2 < 1) bk2 = 1;
for (int i = 1; i <= n; i++)
if (T[i]*5 >= bk)
g[++top] = i;
for (int i = 1; i <= q; i++)
scanf("%d%d%d", &qy[i].x, &qy[i].y, &qy[i].k), qy[i].id = i;
sort(qy+1, qy+q+1);
memset(T, 0, sizeof T);
register int L = 1, R = 0;
for (int i = 1; i <= q; i++) {
while (L < qy[i].x) T[a[L++]]--;
while (L > qy[i].x) T[a[--L]]++;
while (R > qy[i].y) T[a[R--]]--;
while (R < qy[i].y) T[a[++R]]++;
int ans = INT_MAX, cnt = (qy[i].y-qy[i].x+1)/qy[i].k+1;
if (qy[i].y-qy[i].x+1 <= bk) {
for (register int j = qy[i].x; j <= qy[i].y; j++)
if (T[a[j]] >= cnt)
ans = min(ans, a[j]);
} else {
for (register int j = 1; j <= top; j++)
if (T[g[j]] >= cnt)
ans = min(ans, g[j]);
}
p[qy[i].id] = ans==INT_MAX?-1:ans;
}
for (int i = 1; i <= q; i++)
printf("%d\n", p[i]);
return 0;
}