cf1697 E. Coloring
题意:
用 种颜色给 个点染色,每种颜色可重复使用,不必用上所有颜色。距离为曼哈顿距离,要求:
- 若有三点同色则任两点间的距离相等
- 若点 同色而点 不同色,则
问染色方案数取模
思路:
考虑把所有点划分成若干集合,每个集合中的所有点要么同色,要么全都不同色。不属于同一集合的两点必不同色。所在集合的大小为1的点称为孤立点
表示 到其他点的最短距离
如果 ,那么 要么属于同一集合,要么都是孤立点。这一性质是 “双向” 的,我们先用它来构造初始集合
如果 ,那么 要么是孤立的,要么在 的集合中;否则 和 不可能在同一集合。我们用这一性质来拆散不合法的集合
然后把所有集合的大小拿出来做dp。 表示处理到第 个集合,要用 种颜色。那么
答案是 , 为集合总数,要乘一个排列数给它们上色。
用并查集维护集合。代码看起来繁琐,但其实构造集合的思路极其简单,后面的dp也是入门级别
void chai(int x) { //把并查集中的每个点拆成独立
for(int i = 1; i <= n; i++)
if(get(i) == x) p[i] = i;
}
void sol() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];
for(int i = 1; i <= n; i++) { //距离矩阵
mind[i] = INF; //离i最近的点的距离
for(int j = 1; j <= n; j++) if(i != j)
d[i][j] = abs(x[i]-x[j]) + abs(y[i]-y[j]),
mind[i] = min(mind[i], d[i][j]);
}
for(int i = 1; i <= n; i++) //构造集合
for(int j = i+1; j <= n; j++)
if(d[i][j] == mind[i] && d[i][j] == mind[j])
merge(i, j);
for(int i = 1; i <= n; i++) //拆散不合法的集合
for(int j = 1; j <= n; j++) if(i != j)
if(d[i][j] == mind[i] && get(i) != get(j) ||
d[i][j] != mind[i] && get(i) == get(j)) chai(get(i));
for(int i = 1; i <= n; i++) cnt[get(i)]++; //最终的集合
for(int i = 1; i <= n; i++)
if(cnt[i]) cnt[++m] = cnt[i]; //去掉零
f[0][0] = 1; for(int i = 1; i <= m; i++) //dp
for(int j = 1; j <= n; j++) {
(f[i][j] += f[i-1][j-1]) %= mod;
if(cnt[i] > 1 && j >= cnt[i])
(f[i][j] += f[i-1][j-cnt[i]]) %= mod;
}
ll ans = 0; for(int i = 1; i <= n; i++) //统计答案
(ans += A(n, i) * f[m][i] % mod) %= mod;
cout << ans;
}
标签:
思维
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)