orz qyc
看成一个人 向 连边,每个点的入度出度都为 。那么就是若干个环,每次可以选择一条边将这条边两端的端点染色,求 次染色后,最大和最小有颜色点的个数数。
最大值发现可以贪心,偶数长度的环可以用长度除以 次染色全部染色,每次染色的贡献都拉满了。奇数长度的环可以用长度减一除以 次染色将除了一个点其他全部染色,剩下的一个点最后还有染色机会再染。
最小值的话,每个环第一次开始染它色一定是贡献为 ,基于贪心先不开其他的环,再继续对这个环染色,每次贡献为 ,到最后一步贡献为 。换而言之,开了一个环就将它染色到底。
发现对于一个长度为 的环,将其全部染色次数为 ,贡献为 。那么一定想让若干个 组成 ,否则 多开的没染完色的那个环会多贡献 。
问题转化为给定若干个正整数 ,其和为 ,问能不能挑出一些 使得和为 。也就是 01 背包可行性问题。
发现 bitset
和分治 NTT 都不大能过,注意到对 总和的限制,不同的 仅有 个,把多个同样的物品合成一个物体有不同的次数,就变成了一个多重背包问题。
再加个二进制分组优化,时间复杂度大概是个 ,加上那个 其实跑不大满,就冲过去了。
有一个有理有据的方法是类似根号分治,选择一个值域 ,小于等于 的数跑 的多重背包可行性,每次枚举物品重量 之后再记录一个 代表用重量 的物品, 最少被几个 凑出来,就能 dp 了。 的用之前说到的 01 背包可行性问题,复杂度是 的,也就是 的数不会超过 个,加上 bitset 优化即可。
后面那个做法的复杂度为 ,将 设为 发现计算次数可以接受,加上这个很难会被卡满,就过了。
代码是前面没有根号分治的做法。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<bitset>
typedef long long ll;
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T> T Abs(T x) { return x < 0 ? -x : x; }
template <typename T> T chkmax(T &x, T y) { return x = x > y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = (r << 3) + (r <<1) + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 1000010;
int n, p[N], a[N], m, k, ans1, ans2;
bool vis[N];
int ct[N], b[N], c[N], us[N];
std::bitset<1000001>f;
void dfs(int x, int s) {
if(vis[x]) {
a[++m] = s;
return ;
}
vis[x] = 1;
dfs(p[x], s+1);
}
signed main() {
read(n); read(k); int _n = n;
for(int i = 1; i <= n; ++i) read(p[i]);
for(int i = 1; i <= n; ++i)
if(!vis[i])
dfs(i, 0);
n = m;
std::sort(a + 1, a + n + 1);
int k_ = k;
for(int i = 1; i <= n; ++i) {
if(!k_) break ;
int t = Min(a[i] / 2, k_);
ans2 += t * 2; k_ -= t;
}
for(int i = 1; i <= n; ++i)
if((a[i] & 1) && k_)
++ans2,
--k_;
//
for(int i = 1; i <= _n; ++i) ct[a[i]]++;
m = 0;
for(int i = 1; i <= _n; ++i)
if(ct[i])
b[++m] = i,
c[m] = ct[i];
f[0] = 1;
for(int i = 1; i <= m; ++i) {
int tc = c[i];
for(int j = 1; tc; j <<= 1) {
int t = Min(j, tc);
f |= f << (b[i] * t),
tc -= t;
}
}
if(f[k]) ans1 = k;
else ans1 = k+1;
printf("%d %d\n", ans1, ans2);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?