题解 P8398 [CCC2022 S4] Good Triplets
显然,答案不好直接求,我们考虑用总数减去不合法的方案数。
为了不算重,我们每次只考虑当前点与圆心连线交圆周于一点所形成的半圆内的不合法情况,然后用组合数算出剩下两个点的选择方案数。
我们可以用前缀和记录区间内点的数量,然后对于一个点可以算出它的对称点,两段区间拼起来就可以算出半圆内的点数。
最后还要减去退化的三角形,一种是三个点在同一位置,另一种是有两个点在同一位置。
值得注意的是,如果周长为奇数,对称点以及半圆的划分会略有不同,需要特判一下。
复杂度线性。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll n, c, a[N], sum[N], ans, cnt[N];
ll getnum(ll w, ll w2) { //计算区间内点的数量
ll num;
if (w2 > w) num = sum[c - 1] - sum[w2 - 1] + sum[w];
else {
if (w2 > 0) num = sum[w] - sum[w2 - 1];
else num = sum[w];
}
return num;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> c;
ans = n * (n - 1) * (n - 2) / 6; //总数
for (int i = 1; i <= n; ++i) cin >> a[i], ++sum[a[i]], ++cnt[a[i]];
for (int i = 1; i < c; ++i) sum[i] += sum[i - 1];
for (int i = 1; i <= n; ++i) {
ll w = a[i], w2, num;
w2 = (w + c / 2) % c; //对称点
if (c % 2) w2 = (w2 + 1) % c;
num = getnum(w, w2);
num -= cnt[w];
ans -= num * (num - 1) / 2;
}
for (int i = 0; i < c; ++i) { //减去退化的三角形
if (cnt[i] >= 3) ans -= cnt[i] * (cnt[i] - 1) * (cnt[i] - 2) / 6; //三点重合
if (cnt[i] >= 2) { //两点重合
ll w = i, w2 = (i + c / 2) % c, num;
if (c % 2) w2 = (w2 + 1) % c;
num = getnum(w, w2);
if (c % 2 == 0) {
num -= cnt[w] + cnt[w2];
ans -= cnt[i] * (cnt[i] - 1) / 2 * num;
} else {
num -= cnt[w];
ans -= cnt[i] * (cnt[i] - 1) / 2 * num;
}
}
}
cout << ans << endl;
return 0;
}
本文作者:HQJ2007
本文链接:https://www.cnblogs.com/HQJ2007/p/17561276.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现