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;
}
posted @ 2026-07-22 19:38  kzssCCC  阅读(1)  评论(0)    收藏  举报