【题解】NOIP 13连测 #2
NOIP 13连测 #2
A
手动模拟可以发现:
\(a_{i-2},a_i,a_{i+2}\) 总是相邻的,且 \(a_1,a_2\) 是相邻的,\(a_n\) 在最左端
- 若 \(n\) 为奇数,答案为:\(a_na_{n-2}\cdots a_1 +a_2a_4\cdots a_{n-1}\)
- 若 \(n\) 为偶数,答案为:\(a_na_{n-2}\cdots a_2+a_1a_3\cdots a_{n-1}\)
时间复杂度 \(\mathcal{O}(n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, a[N];
int main() {
freopen("reverse.in", "r", stdin);
freopen("reverse.ans", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
if (n & 1){
for (int i = n; i > 0; i -= 2)
cout << a[i] << ' ';
for (int i = 2; i <= n; i += 2)
cout << a[i] << ' ';
}
else{
for (int i = n; i > 0; i -= 2)
cout << a[i] << ' ';
for (int i = 1; i <= n; i += 2)
cout << a[i] << ' ';
}
return 0;
}
B
如果一条边不在图中一棵最小生成树中,那么加入一条新边之后的新图中也可以不用考虑这条边了。(考虑kruskal将边排序的过程)
对原图使用 prim 求出最小生成树的 \(n-1\) 条边,与新加入的 \(q\) 条边排序后,做次 \(q\) 次 kruskal 即可。
时间复杂度 \(\mathcal{O}(n^2+nq\log n)\)。(\(\log n\)为并查集)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10;
int n, q, px[N], py[N], cnt, id[N];
bool vis[N];
ll d[N];
struct Edge{
int u, v, t;
ll w;
bool operator<(const Edge &tmp) const { return w < tmp.w; }
} e[N];
ll cal(int x) { return abs(1ll * x * x * x); }
ll dis(int u, int v) { return cal(px[u] - px[v]) + cal(py[u] - py[v]); }
void add(int u, int v, ll w, int t) { e[++cnt] = (Edge){u, v, t, w}; }
void prim(){
memset(d, 0x3f, sizeof(d));
d[1] = 0;
for (int i = 1; i <= n; i++){
int u = 0;
for (int j = 1; j <= n; j++)
if (!vis[j] && (!u || d[u] > d[j]))
u = j;
vis[u] = 1;
if (id[u])
add(u, id[u], d[u], 0);
for (int j = 1; j <= n; j++)
if (!vis[j] && d[j] > dis(u, j)){
d[j] = dis(u, j);
id[j] = u;
}
}
}
int fa[N];
ll ans;
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void solve(int t){
for (int i = 1; i <= n; i++)
fa[i] = i;
ans = 0;
for (int i = 1; i <= cnt; i++){
if (e[i].t > t)
continue;
int u = find(e[i].u), v = find(e[i].v);
if (u != v){
ans += e[i].w;
fa[u] = v;
}
}
cout << ans << '\n';
}
int main(){
freopen("logistics.in", "r", stdin);
freopen("logistics.ans", "w", stdout);
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> px[i] >> py[i];
prim();
for (int i = 1; i <= q; i++){
ll w;
int u, v;
cin >> u >> v >> w;
add(u, v, w, i);
}
sort(e + 1, e + cnt + 1);
for (int i = 0; i <= q; i++)
solve(i);
return 0;
}
C
很水的题目,显然是排好序后答案最小。
当然也可以用 \(n-环数\)。
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 300010, P = 998244353;
int n, T;
int rk[N];
PII w[N], d[N];
signed main() {
freopen("sequence.in", "r", stdin);
freopen("sequence.ans", "w", stdout);
cin >> n >> T;
for (int i = 1; i <= n; i++) scanf("%d", &w[i].x), w[i].y = i;
for (int i = 1; i <= n; i++) scanf("%d", &d[i].x), d[i].y = i;
sort(w + 1, w + n + 1), sort(d + 1, d + n + 1);
for (int i = 1; i <= n; i++) rk[w[i].y] = i;
// 位置在 w[i].y 上的排名第 i
LL res = 0, cnt = 0;
for (int i = 1; i <= n; i++) {
int t = d[i].y; // 第 i 小的数要到位置 t
if (w[i].y != t) {
int r = rk[t]; // 位置 t 上的是哪个
swap(rk[t], rk[w[i].y]);
swap(w[i].y, w[r].y);
cnt++;
}
(res += 1ll*(d[i].x - w[i].x)*(d[i].x - w[i].x)) %= P;
}
cout << res;
if (T)
cout << " " << cnt << endl;
return 0;
}
D
枚举方阵的大小为 \(k\) ,每行给定一个权值 \(x_{i}\) ,每列给定一个权值 \(y_{i}\) ,那么 \(a_{i, j}=x_{i}+y_{j}\) 是一个合法的方阵。
一个合法的方阵也一定可以由一组 \(x_{i}, y_{i}\) 表示。 \(\left(x_{i}=a_{i, 1}, y_{j}=a_{1, j}-a_{1,1}\right)\)
但是 \(x_{i}^{\prime}=x_{i}+1, y_{i}^{\prime}=y_{i}-1\) 与 \(x_{i}, y_{i}\) 对应的方阵相同。
令 \(\min \left(x_{i}\right)=0\) 则方阵与数列之间——对应。
此时 \(a_{i, j} \geq m\) 等价于 \(\min \left(y_{i}\right) \geq m\) 。
将 \(y_{i}\) 减去 \(m\) ,问题变为将至多 \(n-k * m\) 个石头放入 \(2 k\) 个格子里,且前 \(k\) 个格子至少有一个没有石子的方案数。
容斥求得答案为 \(\binom{n-k * m+2 * k}{2 * k}-\binom{n-k * m+k}{2 * k}\)
最后对所有可能的 \(k\) 求和。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
int n, m, fac[N], inv[N];
int fpow_(int a, int b, int res = 1){
for (; b; b >>= 1, a = 1ll * a * a % mod)
if (b & 1)
res = 1ll * res * a % mod;
return res;
}
void init_(int n){
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[n] = fpow_(fac[n], mod - 2);
for (int i = n; i; i--)
inv[i - 1] = 1ll * inv[i] * i % mod;
}
int C_(int n, int k) { return n < k ? 0 : 1ll * fac[n] * inv[k] % mod * inv[n - k] % mod; }
void work_(){
int ans = 0;
for (int i = 1; 1ll * i * m <= n; i++){
int tot = n - i * m;
ans = (ans + C_(tot + 2 * i, 2 * i)) % mod;
ans = (ans - C_(tot + i, 2 * i)) % mod;
}
printf("%d\n", (ans % mod + mod) % mod);
}
int main(){
freopen("magic.in","r",stdin);
freopen("magic.ans","w",stdout);
init_(2e5);
int T;
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
work_();
}
return 0;
}