CF1783
构造题。
如果元素互不相同,我们可以降序排列。
如果有相同元素的话,就多构造几个降序排列拼在一起,可以证明符合条件。
复杂度 。
还是构造题。
我们思考是否对于任意一个 都可以构造出一个矩形使得相邻之差包含 到 。
然后发现可以。
先构造出一个这样的序列:
然后 型将其放入 的矩形即可。
复杂度 。
贪心。
我最初步的想法是将 从大到小排序,然后尽可能多选。
但是我们发现其他球队胜的次数会根据我们选的方法而改变,所以这是不行的。
假设最多能胜 场,那么容易发现可能会影响最终排名的只有球队 和球队 。
于是我们必选 ,贪一下,必选 ,贪一下,必选 值最小的,贪一下。
三种情况取最优排名即可。
复杂度 。
#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。
定义 为已经处理完前 项,现在考虑第 项,且 的情况下最终能生成的序列的个数。
容易发现,当 时序列不会发生变化,而 时有产生两种情况。
所以转移方程为:
边界为 。
注意,第二维 的范围是 ,所以要加上一个数平移一下。
复杂度 。
#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;
}
第一次读错题了,以为尝试数不清空......
如果 ,不用管。
如果 ,考虑什么情况下 。
然后我们发现如果 在 中有倍数,那么 不能选。
于是可以将所有的 标记一下,差分 解决,然后直接枚举每一个 看是否可行即可。
复杂度调和级数 。
#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。
我们可以把序列上的元素交换变为图的形式。
考虑只有一个序列的情况。
我们可以在 和 之间连一条边,最小交换次数为总点数减去环的个数。
原因很显然,对于一个环,我们交换完其中 个元素后,最后一个元素肯定在它该在的位置。
如果有两个序列呢?
我们可以“固定”一些数,也就是上述的环中最后一个元素。
如果我们固定 ,那么 在两张图中的环中的元素就不能再选了。
然后发现这就是一个裸的二分图最大匹配问题。
建图直接跑 dinic 就行了。
注意,由于序列中每个元素只会有一条入边和一条出边,所以构成的图一定是若干个简单环,这样就可以直接用并查集找了。
复杂度 。
#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;
}
本文作者:HQJ2007
本文链接:https://www.cnblogs.com/HQJ2007/p/17561581.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下