A-B:略
C:可能题意比较绕,第i个答案就是穿着i这个号码(也就是Q[j] = i,这个时候j这个位置),看向的那个人的号码(也就是P[j])
代码:
void solve()
{int n;cin >> n;vi p(n + 1), q(n + 1), ans(n + 1);rep(i, 1, n) {cin >> p[i];}rep(i, 1, n) {cin >> q[i];}rep(i, 1, n) {ans[q[i]] = q[p[i]];}rep(i, 1, n) {cout << ans[i] << ' ';}
}
D:枚举哪两个集合[i,j]计算贡献,用map记录每个集合每个元素a个数,最后枚举i和j中间最小那个map计算贡献即可。
void solve()
{int n;cin >> n;vector<map<int, int>> cnt(n + 1);vi k(n + 1);rep(i, 1, n) {cin >> k[i];int x;rep(j, 1, k[i]) {cin >> x;cnt[i][x]++;}}double ans = 0;rep(i, 1, n) {rep(j, i + 1, n) {if (sz(cnt[i]) < sz(cnt[j])) {double dw = 1.0 * k[i] * k[j];double up = 0;for (auto &[x, c] : cnt[i]) {if (cnt[j].count(x)) {up += 1.0 * cnt[j][x] * c;}}ans = max(ans, up / dw);} else {double dw = 1.0 * k[i] * k[j];double up = 0;for (auto &[x, c] : cnt[j]) {if (cnt[i].count(x)) {up += 1.0 * cnt[i][x] * c;}}ans = max(ans, up / dw);}}}cout << fixed << setprecision(10) << ans << '\n';
}
E:考虑用并查集把没用到的边筛除出来,然后每次根据没用到的边再根据所在连通块合并即可,直到最后只剩下一个连通块。
void solve()
{int n, m;cin >> n >> m;DSU dsu(n);vector<pii> edges(m + 1);vi vec;rep(i, 1, m) {int u, v;cin >> u >> v;edges[i] = {u, v};if (dsu.same(u, v)) {vec.pb(i);} else {dsu.merge(u, v);}}if (dsu.getBlocks() == 1) {cout << 0 << '\n';return;}set<int> st;rep(i, 1, n) {if (i == dsu.find(i)) {st.insert(i);}}vector<tuple<int, int, int>> ans;for (auto &i : vec) {auto [u, v] = edges[i];int t = v;u = dsu.find(u);v = dsu.find(v);st.erase(u);int z = *st.begin();st.erase(z);dsu.merge(u, z);ans.pb({i, t, z});st.insert(dsu.find(u));if (sz(st) == 1) {break;}}cout << sz(ans) << '\n';for (auto &[i, x, y]: ans) {cout << i << ' ' << x << ' ' << y << '\n';}
}
F:因为前面的总会被后面的顶掉,所以我们直接倒置放,一定不会被顶掉,每次从头开始找到未被放数字的第a[i]个位置,这个位置就是当前数字,寻找的过程可以通过维护线段树,每次放一个数字就可以把这个位置上的sum标成1,在线段树上二分这个sum去寻找位置,复杂度nlogn。
const int N = 5e5 + 5;
struct node {int l, r;int sum;
} tr[N << 2];void push_up(int u) {tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}void build(int u, int l, int r) {tr[u] = {l, r, 0};if (l == r) {tr[u].sum = 1;return;}int mid = (l + r) >> 1;build(u << 1, l, mid);build(u << 1 | 1, mid + 1, r);push_up(u);
}void modify(int u, int p) {if (tr[u].l == tr[u].r) {tr[u].sum = 0;return;}int mid = (tr[u].l + tr[u].r) >> 1;if (p <= mid) {modify(u << 1, p);} else {modify(u << 1 | 1, p);}push_up(u);
}int find_pos(int u, int sum) {if (tr[u].l == tr[u].r) {return tr[u].l;}if (tr[u << 1].sum >= sum) {return find_pos(u << 1, sum);}return find_pos(u << 1 | 1, sum - tr[u << 1].sum);
}void solve()
{int n;cin >> n;vi a(n + 1);vi ans(n + 1);rep(i, 1, n) {cin >> a[i];}build(1, 1, n);per(i, n, 1) {int p = find_pos(1, a[i]);ans[p] = i;modify(1, p);}rep(i, 1, n) {cout << ans[i] << ' ';}
}
G:经典FFT,考虑把值域a[i]看成多项式的x^a[i],把方案数看成系数,最后把多项式相乘系数就是每个值的方案数,由于原式子可以化成A+C=2B,因此我们枚举2B,只可能是偶数,且B一定要出现在原来的a数组中,这就可以计算到答案中去。
void solve()
{vl A(1e6 + 1), B(1e6 + 1), C(1e6 + 1);int n;cin >> n;rep(i, 1, n) {int x;cin >> x;A[x]++;B[x]++;C[x]++;}auto ret = atcoder::convolution_ll(A, B);ll ans = 0;REP(i, 2, 2000000, 2) {if (C[i >> 1]) {ans += ret[i] / 2;}}cout << ans;
}