CF1783
构造题。
如果元素互不相同,我们可以降序排列。
如果有相同元素的话,就多构造几个降序排列拼在一起,可以证明符合条件。
复杂度 \(O(n)\)。
还是构造题。
我们思考是否对于任意一个 \(n\) 都可以构造出一个矩形使得相邻之差包含 \(1\) 到 \(n^2-1\)。
然后发现可以。
先构造出一个这样的序列:\(1,n^2,2,n^2-1,3,n^2-2......\)
然后 \(S\) 型将其放入 \(n\times n\) 的矩形即可。
复杂度 \(O(n^2)\)。
贪心。
我最初步的想法是将 \(a\) 从大到小排序,然后尽可能多选。
但是我们发现其他球队胜的次数会根据我们选的方法而改变,所以这是不行的。
假设最多能胜 \(k\) 场,那么容易发现可能会影响最终排名的只有球队 \(k+1\) 和球队 \(k+2\)。
于是我们必选 \(k+1\),贪一下,必选 \(k+2\),贪一下,必选 \(a\) 值最小的,贪一下。
三种情况取最优排名即可。
复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int T, n, m, b[N], c[N], vis[N];
struct node{
int a, id;
}x[N];
bool cmp(node xx, node yy) {
return xx.a < yy.a;
}
int getpai(int cnt) {
for (int i = 1; i <= n; ++i) c[i] = i - 1 + (vis[i] == 0);
int pai = 0;
for (int i = 1; i <= n; ++i) {
if (c[i] > cnt) {
++pai;
}
}
return pai + 1;
}
int solve(int w) {
if (w > n || m < b[w]) return INT_MAX;
for (int i = 1; i <= n; ++i) vis[i] = 0;
int t = m - b[w], cnt = 1;
vis[w] = 1;
for (int i = 1; i <= n; ++i) {
if (x[i].id == w) continue;
if (x[i].a <= t) {
t -= x[i].a;
++cnt;
vis[x[i].id] = 1;
}
}
return getpai(cnt);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> x[i].a;
b[i] = x[i].a;
x[i].id = i;
vis[i] = 0;
}
sort(x + 1, x + n + 1, cmp);
int cnt = 0, t = m;
for (int i = 1; i <= n; ++i) {
if (t >= x[i].a) {
t -= x[i].a;
++cnt;
vis[x[i].id] = 1;
}
}
int ans = getpai(cnt);
ans = min(ans, min(solve(cnt + 1), solve(cnt + 2)));
cout << ans << endl;
}
return 0;
}
怪怪的 DP。
定义 \(f_{i,j}\) 为已经处理完前 \(i-1\) 项,现在考虑第 \(i\) 项,且 \(a_{i+1}=j\) 的情况下最终能生成的序列的个数。
容易发现,当 \(j=0\) 时序列不会发生变化,而 \(j\neq0\) 时有产生两种情况。
所以转移方程为:
边界为 \(f_{n-1,j}=1\)。
注意,第二维 \(j\) 的范围是 \([-na,+na]\),所以要加上一个数平移一下。
复杂度 \(O(n^3)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 300 + 5, nn = 300 * 300, mod = 998244353;
int n, a[N];
ll f[N][N * N * 2];
ll dfs(int i, int j) {
if (i == n - 1) return f[i][j + nn] = 1;
if (f[i][j + nn] != -1) return f[i][j + nn];
if (j) f[i][j + nn] = (dfs(i + 1, a[i + 2] - j) + dfs(i + 1, a[i + 2] + j)) % mod;
else f[i][j + nn] = dfs(i + 1, a[i + 2]);
return f[i][j + nn];
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
memset(f, -1, sizeof(f));
cout << dfs(1, a[2]) << endl;
return 0;
}
第一次读错题了,以为尝试数不清空......
如果 \(a_i\le b_i\),不用管。
如果 \(a_i>b_i\),考虑什么情况下 \(\lfloor\frac{a_i}{k}\rfloor>\lfloor\frac{b_i}{k}\rfloor\)。
然后我们发现如果 \(k\) 在 \([b_i,a_i)\) 中有倍数,那么 \(k\) 不能选。
于是可以将所有的 \([b_i,a_i)\) 标记一下,差分 \(O(1)\) 解决,然后直接枚举每一个 \(k\) 看是否可行即可。
复杂度调和级数 \(O(n\log n)\)。
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int N = 1e6 + 5;
int T, n, a[N], b[N], vis[N];
vector<int> ans;
int main() {
cin >> T;
while (T--) {
n = read();
ans.clear();
for (int i = 1; i <= n; ++i) a[i] = read(), vis[i] = 0;
for (int i = 1; i <= n; ++i) b[i] = read();
for (int i = 1; i <= n; ++i) {
if (a[i] > b[i]) {
++vis[b[i]];
--vis[a[i]];
}
}
for (int i = 1; i <= n; ++i) vis[i] += vis[i - 1];
for (int x = 1; x <= n; ++x) {
bool flag = 1;
for (int i = x; i <= n; i += x) {
if (vis[i]) flag = 0;
}
if (flag) ans.push_back(x);
}
cout << ans.size() << endl;
for (int i = 0; i < ans.size(); ++i) printf("%d ", ans[i]);
cout << endl;
}
return 0;
}
不会,看了眼题解,明白了一个小 trick。
我们可以把序列上的元素交换变为图的形式。
考虑只有一个序列的情况。
我们可以在 \(i\) 和 \(a_i\) 之间连一条边,最小交换次数为总点数减去环的个数。
原因很显然,对于一个环,我们交换完其中 \(x-1\) 个元素后,最后一个元素肯定在它该在的位置。
如果有两个序列呢?
我们可以“固定”一些数,也就是上述的环中最后一个元素。
如果我们固定 \(u\),那么 \(u\) 在两张图中的环中的元素就不能再选了。
然后发现这就是一个裸的二分图最大匹配问题。
建图直接跑 dinic 就行了。
注意,由于序列中每个元素只会有一条入边和一条出边,所以构成的图一定是若干个简单环,这样就可以直接用并查集找了。
复杂度 \(O(n^2)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5, inf = INT_MAX >> 2;
int n, a[N], b[N], id[N], id2[N], cnt = 0, fa[N], s, t, head[N], nex[N], to[N], val[N], dep[N], cur[N], fm[N], no[N], tot = 1;
int ff(int x) {return fa[x] == x?x:fa[x] = ff(fa[x]);}
void add(int u, int v) {
val[++tot] = 1;
to[tot] = v;
nex[tot] = head[u];
head[u] = tot;
val[++tot] = 0;
to[tot] = u;
nex[tot] = head[v];
head[v] = tot;
}
queue<int> q;
int bfs() {
for (int i = 1; i <= t; ++i) dep[i] = 0, cur[i] = head[i];
dep[s] = 1; q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = head[u]; i; i = nex[i]) {
int v = to[i];
if (val[i] && !dep[v]) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[t];
}
int dfs(int u, int c) {
if (u == t) return c;
int out = 0;
for (int i = cur[u]; i && c; i = nex[i]) {
cur[u] = i;
int v = to[i];
if (val[i] && dep[v] == dep[u] + 1) {
int tmp = dfs(v, min(c, val[i]));
val[i] -= tmp; val[i ^ 1] += tmp; out += tmp; c -= tmp;
}
}
if (!out) dep[u] = 0;
return out;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
s = 2 * n + 1; t = s + 1;
for (int i = 1; i <= n; ++i) cin >> a[i], fa[i] = i;
for (int i = 1; i <= n; ++i) cin >> b[i];
for (int i = 1; i <= n; ++i) fa[ff(i)] = ff(a[i]);
int num = 0;
for (int i = 1; i <= n; ++i) {
if (i == ff(i)) {
id[i] = ++num;
add(s, num);
}
}
for (int i = 1; i <= n; ++i) id[i] = id[ff(i)];
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= n; ++i) fa[ff(i)] = ff(b[i]);
for (int i = 1; i <= n; ++i) {
if (i == ff(i)) {
id2[i] = ++num;
add(num, t);
}
}
for (int i = 1; i <= n; ++i) id2[i] = id2[ff(i)];
for (int i = 1; i <= n; ++i) {
add(id[i], id2[i]);
fm[i] = tot - 1;
}
int ans = n;
while (bfs()) ans -= dfs(s, inf);
cout << ans << endl;
for (int i = 1; i <= n; ++i) {
if (!val[fm[i]]) {
no[i] = 1;
}
}
for (int i = 1; i <= n; ++i) {
if (!no[i]) {
cout << i << " ";
}
}
cout << endl;
return 0;
}