AtCoder-abc466_f Many Mod Calculation 题解

形式化题意

\f(x)=(\\dots((x\\bmod A_1)\\bmod A_2)\\dots)\\bmod A_n \\

即依次对 \(A_1,A_2,\dots,A_n\) 取模。

\\\sum_{k=1}\^{X}\[f(k)=0 \]

的值。

Solution

考虑所有 \(x = 0,1,\dots,X\) 依次对 \(A_1,A_2,\dots,A_N\) 取模的过程。初始时这些数构成一个半开区间 \([0, X+1)\)。第 \(i\) 步对 \(A_i\) 取模,会把当前每个区间 \([0,d)\) 分拆成:

  • \(\lfloor d/A_i\rfloor\) 个 \([0,A_i)\)
  • \(1\) 个 \([0,d\bmod A_i)\)(若 \(d\bmod A_i\neq 0\)

我们可以维护所有区间并将右端点相同的区间合并。具体地,用一个 map 维护每个右端点 \(d\) 对应的区间 \([0,d)\) 出现次数。然后直接按照上述过程模拟即可。

复杂度分析:第 \(i\) 步中,对于区间 \([0,d)\) 取模 \(A_i\) 时若 \(d\ge A_i\) 则必有 \(d\bmod A_i<d/2\)。这一步还会产生 \([0,A_i)\) 区间。因此在整个过程中出现过的本质不同区间共有 \(O(N\log X)\) 个,因此总时间复杂度 \(O(N\log X\log N)\)。

Code

cpp 复制代码
#include <bits/stdc++.h>
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define fi first
#define se second
#define int long long
using namespace std;
constexpr int N=2e5+5;
int n,x,a[N];
map<int,int,greater<int>> mp;
void solve(){
    cin>>n>>x;
    mp.clear();
    mp[x+1]=1;
    rept(i,1,n) cin>>a[i];
    rept(i,1,n){
        for(auto it=mp.begin();it!=mp.end();++it){
            if(it->fi<=a[i]) break;
            int q=it->fi/a[i],r=it->fi%a[i];
            mp[a[i]]+=q*it->se;
            if(r) mp[r]+=it->se;
        }
        while(mp.begin()->fi>a[i]) mp.erase(mp.begin());
    }
    int ans=0;
    for(auto it=mp.begin();it!=mp.end();++it){
        ans+=it->se;
    }
    cout<<ans-1<<'\n';
}
signed main(){
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}