2025 ICPC武汉邀请赛 C 题思路分享
题意
有 \(n\) 个格子初始长满杂草,从 \(1\) 出发,到达一个格子之后会花 \(1\) 小时检查并除去该格子的杂草,走到 \(n\) 返回,除去第 \(n\) 个格子,其他格子会被检查 \(2\) 次.
第 \(i\) 个格子杂草被除去后 \(a_i\) 小时 \(1\) 分钟又重新长出,总共走 \(m\) 轮,求每轮除去杂草的数量.
\(1\le n,m \le 10^6\).
思路
独立考虑每个格子对轮次的贡献.
记从 \(1\) 到 \(n\) 除去杂草的事件为 \(I\),从 \(n\) 到 \(1\) 除去杂草事件为 \(O\),可以发现只有 \(3\) 情况:
-
\(III\cdots\)
-
\(IOOO\cdots\)
-
\(IOIOIO\cdots\)
令 \(L = 2n-1\) 表示周期, \(A = 2(n-i)\) 表示从 \(i\) 走到 \(n\) 又返回 \(i\) 的时间,\(B = 2i-1\) 表示从 \(i\) 走到 \(1\) 又返回 \(i\) 的时间,令 \(a_i+1 = cL+r\).
-
\(III\cdots\)
第一种情况 \(r=0\),在 \(1,1+c,1+2c,\cdots\) 轮次贡献.
第二种情况 \(r\gt A\),在 \(1,2+c,3+2c,\cdots\) 轮次贡献.
-
\(IOOO\cdots\)
\(1\le r \le A\) 且 \(r\gt B\),在 \(1,c+1,2(c+1),3(c+1)\cdots\) 轮次贡献.
-
\(IOIOIO\cdots\)
\(1\le r \le A\) 且 \(r\le B\),\(I\) 在 \(1,2+2c,3+4c,\cdots\) 轮次贡献,\(O\) 在 \(1+c,2+3c,3+5c\) 轮次贡献.
缓存一下起点和差值,暴力跳是调和级数的.
时间复杂度 \(\mathcal{O}(n+m\log m)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve(){
int n,m;
cin >> n >> m;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
}
int L = 2*n-1;
vector<int> res(m+1);
map<pair<int,int>,int> mp;
for (int i=1;i<=n;i++){
int A = 2*(n-i);
int B = 2*(i-1)+1;
int c = (a[i]+1)/L;
int r = (a[i]+1)%L;
if (r==0){
mp[{1,c}]++;
}
else if (r>A){
mp[{1,c+1}]++;
}
else if (r>=1 && r<=A && r>B){
res[1]++;
mp[{c+1,c+1}]++;
}
else{
mp[{1,2*c+1}]++;
mp[{c+1,2*c+1}]++;
}
}
for (auto& [pr,v]:mp){
auto& [s,d] = pr;
for (int x=s;x<=m;x+=d){
res[x] += v;
}
}
for (int i=1;i<=m;i++){
cout << res[i] << ' ';
}
cout << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号