1 条题解
-
0
题意简述
有 个灯,第 个灯在时刻 安装并立即点亮,之后每隔 分钟切换一次状态(开→关→开→…)。即对于整数 ,灯在时间段 内是亮的。求最早的时刻 ,使得所有灯都亮着,或判断无解。
数学转化
灯 在时刻 亮着,当且仅当 。
设 ,则条件等价于 落在区间 内(模 意义下的循环区间)。同时 必须不小于所有 ,即 。
于是问题变为:找到一个整数 ,使得 被所有 覆盖,并且满足 且 的最小 。
算法
- 求出 。
- 使用差分数组 记录区间覆盖。对于每个 :
- ;
- ,;
- 若 ,则 ,;
- 否则,,;,。
- 求前缀和得到每个余数 的覆盖次数 。
- 遍历 ,若 ,则计算候选 :
- 令 。
- 若 ,则 ;
- 否则 。
- 用所有候选 的最小值更新答案。
- 若没有任何 满足 ,输出 ;否则输出最小 。
时间复杂度 ,空间 。由于 且 ,总复杂度在时限内轻松通过。
参考代码
#include <bits/stdc++.h> using namespace std; using ll = long long; void solve() { int n, k; cin >> n >> k; vector<int> a(n); int mx = 0; for (int i = 0; i < n; ++i) { cin >> a[i]; mx = max(mx, a[i]); } int mod = 2 * k; vector<int> diff(mod + 2, 0); // 差分数组 for (int x : a) { int b = x % mod; int L = b; int R = (b + k - 1) % mod; if (L <= R) { diff[L] += 1; diff[R + 1] -= 1; } else { diff[L] += 1; diff[mod] -= 1; // 标记到 mod-1 为止,diff[mod] 用于前缀和 diff[0] += 1; diff[R + 1] -= 1; } } vector<int> cov(mod); int cur = 0; for (int i = 0; i < mod; ++i) { cur += diff[i]; cov[i] = cur; } ll ans = LLONG_MAX; int base = mx - (mx % mod); for (int r = 0; r < mod; ++r) { if (cov[r] == n) { ll x; if (r >= mx % mod) { x = base + r; } else { x = base + mod + r; } ans = min(ans, x); } } if (ans == LLONG_MAX) cout << "-1\n"; else cout << ans << "\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; }
- 1
信息
- ID
- 6906
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者