「赛后总结」Codeforces Round #686 (Div. 3)

题意 & 题解

A.Special Permutation

题意:

找一个 \(1\sim n\) 的排列使得 \(\forall \ i \in[1,n]\)\(a_i \neq i\)。多测。

题解:

小构造。

\(n\) 为偶数,答案是 \(n\sim 1\)

\(n\) 为奇数,先输出中位数再 \(n\sim1\)

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>

int t, n;

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        if (n & 1) {
            printf("%d ", n / 2 + 1);
            for (int i = n; i >= 1; --i) {
                if (i == n / 2 + 1) continue;
                printf("%d ", i);
            }
        }
        else {
            for (int i = n; i >= 1; --i) printf("%d ", i);
        }
        puts("");
    }
    return 0;
}

B.Unique Bid Auction

题意:

找到只出现过一次的最小的数的下标。多测。

题解:

将原数组排序后看是否出现过多次。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 200010

int t, n;
struct qwer {
    int w, id;
    friend bool operator < (qwer q1, qwer q2) {
        return q1.w < q2.w;
    }
}a[M];

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n); int ans = -1;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i].w);
            a[i].id = i;
        }
        std::sort(a + 1, a + n + 1);
        for (int i = n; i >= 1; --i) {
            if (a[i].w != a[i + 1].w && a[i].w != a[i - 1].w) ans = a[i].id;
        }
        for (int i = 1; i <= n; ++i) {
            a[i].w = a[i].id = 0;
        }
        printf("%d\n", ans);
    }
    return 0;
}

C.Sequence Transformation

题意:

给定一个序列,选择序列中一个数 \(x\),每次可以删除一段不含 \(x\) 的区间,问最少多少次序列中只剩下 \(x\)。多测。

题解:

题目其实就是问出现次数最少的数出现了多少次。连续的一段相同的数按一次算,两端的端点不算。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 200001

int min(int a, int b) { return a < b ? a : b; }

int t, n, a[M], cnt[M];

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        memset(cnt, 0, sizeof cnt);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            if (a[i] != a[i - 1]) ++cnt[a[i]];
        }
        --cnt[a[1]], --cnt[a[n]];
        int minn = 2147483647;
        for (int i = 1; i <= n; ++i) {
            minn = min(minn, cnt[a[i]]);
        }
        printf("%d\n", minn + 1);
    }
    return 0;
}

D.Number into Sequence

题意:

给一个数 \(n\),要求找一个长为 \(k\) 的序列,使得 \(\forall \ i\in [2,n]\)\(a_{i-1}\mid a_i\) 并且 \(\prod\limits_{i=1}^{n} a_i = n\)。要求 \(k\) 最大。多测。

题解:

根据唯一分解定理 \(n\) 一定可以写成下面的形式。

\(n = {p_1}^{q_1}\times {p_2}^{q_2}\times \dots\times {p_n}^{q_n}\)

如果没有 \(\forall \ i\in [2,n]\)\(a_{i-1}\mid a_i\) 这个要求直接输出每个质因子就是最长的。这个要求就使得下标小的元素含有的质因子,下标大的元素必须含有。所以最长的 \(k\) 就是 \(\max\limits_{i=1}^{n} q_i\)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
typedef long long ll;

ll t, n;

void solve(ll x) {
    ll k = 0, ans = x, temp = 1, stop = sqrt(x);
    for (int i = 2; i <= stop; ++i) {
        if (x % i == 0) {
            ll k_ = 0;
            while (x % i == 0) {
                ++k_, x /= i;
            }
            if (k_ > k) k = k_, temp = i;
        }
    }
    if (k == 0) k = 1;
    printf("%d\n", k);
    for (int i = 1; i < k; ++i) {
        printf("%lld ", temp);
        ans /= temp;
    }
    printf("%lld\n", ans);
}

int main() {
    scanf("%lld", &t);
    while (t--) {
        scanf("%lld", &n);
        solve(n);
    }
    return 0;
}

E.Number of Simple Paths

题意:

给定 \(n\) 个点 \(n\) 条边的无向连通图,问其中有多少不同的简单路径。多测。

题解:

图是一个环上面挂着多颗树的样子。

对于每颗树内的路径,设树的根节点为 \(v\),以 \(v\) 为根的子树的大小为 \(cnt_v\)(不包含环中除了 \(v\) 的其他点)。路径条数为 \(\dfrac{cnt_v(cnt_v-1)}{2}\)

对于从树上走出去的路径条数有 \(2\times cnt_v(n-cnt_v)\) 只用统计 \(cnt_v(n-cnt_v)\) 防止重复。

统计 \(cnt_v\) 可以使用类似拓扑序的东西。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#define M 200001

int cnt[M];
std::queue<int> q;
int t, n, pthn, head[M], ideg[M];
struct Edge {
    int nxt, to;
}pth[M << 1];

void add(int frm, int to) {
    pth[++pthn].to = to, pth[pthn].nxt = head[frm];
    head[frm] = pthn;
}

void format() {
    for (int i = 1; i <= n; ++i) ideg[i] = 0;
    for (int i = 1; i <= n; ++i) head[i] = 0;
    for (int i = 1; i <= pthn; ++i) pth[i].nxt = pth[i].to = 0;
    pthn = 0;
}

int main() {
    scanf("%d", &t);
    while (t--) {
        format();
        scanf("%d", &n);
        for (int i = 1, u, v; i <= n; ++i) {
            scanf("%d %d", &u, &v);
            add(u, v), add(v, u);
            ++ideg[u], ++ideg[v];
        }
        for (int i = 1; i <= n; ++i) {
            cnt[i] = 1;
            if (ideg[i] == 1) q.push(i);
        }
        while (!q.empty()) {
            int u = q.front(); q.pop();
            for (int i = head[u]; i; i = pth[i].nxt) {
                int v = pth[i].to;
                if (ideg[v] != 1) {
                    --ideg[v], cnt[v] += cnt[u];
                    if (ideg[v] == 1) q.push(v);
                }
            }
        }
        long long ans = 0;
        for (int i = 1; i <= n; ++i) {
            if (ideg[i] != 1) ans += 1ll * cnt[i] * (cnt[i] - 1) / 2 + 1ll * cnt[i] * (n - cnt[i]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

F.Array Partition

题意:

给一个序列 \(a\)。找到三个数 \(x,y,z\) 使得 \(x+y+z = n\) 并且 \(\max\limits_{i=1}^{x}\{a_i\} = \min\limits_{i=x+1}^{x+y}\{a_i\} = \max\limits_{i=x+y+1}^{n}\{a_i\}\)。要求 \(x,y,z > 0\)。多测。

题解:

枚举第一个区间的右端点 \(x\)\(max1 = \max\limits_{i=1}^{x}\{a_i\}\)

二分第二个区间的右端点 \(mid\)\(min1 = \min\limits_{i=x+1}^{mid}\{a_i\}\)

如果 \(max1>min1\) 第二个区间需要缩小。

如果 \(max1<min1\) 第二个区间需要扩大。

如果 \(max1 = min1\) 去判断和第三个区间的关系,\(max2 = \max\limits_{i=mid+1}^{n}\{a_i\}\)

如果 \(max2 = min1\) 代表找到了答案。

如果 \(max2 > min1\) 第二个区间需要扩大。

如果 \(max2 < min1\) 第二哥区间需要缩小。

查询最值使用 \(st\) 表。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 200001

inline void read(int &T) {
	int x = 0;
	bool f = 0;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') f = !f;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	T = f ? -x : x;
}

int min(int a, int b) { return a < b ? a : b; }
int max(int a, int b) { return a > b ? a : b; }

int t, n, lg[M], min_[M][21], max_[M][21];

int qmax(int l, int r) {
	int k = lg[r - l + 1] - 1;
	return max(max_[l][k], max_[r - (1 << k) + 1][k]);
}

int qmin(int l, int r) {
	int k = lg[r - l + 1] - 1;
	return min(min_[l][k], min_[r - (1 << k) + 1][k]);
}

bool solve(int x, int &r) {
	int l = x + 1, max1 = qmax(1, x); r = n - 1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		int min1 = qmin(x + 1, mid), max2 = qmax(mid + 1, n);
		if (min1 == max1) {
			if (max2 == min1) { r = mid; return true; }
			if (max2 > min1) l = mid + 1;
			if (max2 < min1) r = mid - 1;
		}
		if (min1 > max1) l = mid + 1;
		if (min1 < max1) r = mid - 1;
	}
	return false;
}

int main() {
	read(t);
	for (int i = 1; i < M; ++i) {
		lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
	}
	while (t--) {
		read(n);
		for (int i = 1, x; i <= n; ++i) {
			read(x);
			min_[i][0] = max_[i][0] = x;
		}
		for (int j = 1; (1 << j) <= n; ++j) {
			for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
				min_[i][j] = min(min_[i][j - 1], min_[i + (1 << (j - 1))][j - 1]);
				max_[i][j] = max(max_[i][j - 1], max_[i + (1 << (j - 1))][j - 1]);
			}
		}
		int l = -1, r = 0;
		for (int i = 1; i < n - 1; ++i) {
			if (solve(i, r)) { l = i; break; }
		}
		if (l == -1) puts("NO");
		else {
			puts("YES");
			//std::cout << qmax(1, l) << " " << qmin(l + 1, r) << " " << qmax(r + 1, n) << '\n';
			printf("%d %d %d\n", l, r - l, n - r);
		}
	}
	return 0;
}

rating & 总结

没有 rating。

我不太行/kk

posted @ 2020-11-26 08:19  yu__xuan  阅读(122)  评论(0编辑  收藏  举报