题解:P12751 [POI 2017 R2] 集装箱 Shipping containers

cnblogs

题面

第二道根号分治,对初学者来说很友好的一道题。

题意在题面中写的很清楚,这里不多赘述。

思路

先从暴力开始想。

每次暴力的时间复杂度最坏明显是 \(O(n^2)\) 的,因为是类似区间加和最后统计的问题,可以尝试用差分来做。

但普通差分需要枚举 \(d\) 的大小,复杂度仍然是 \(O(n^2)\)

观察到每次暴力时,\(d\) 与枚举的次数成反比,也就是 \(d\) 越大枚举的次数越少。至此,我们可以使用根号分治解决。

设阈值为 \(B\),则暴力复杂度为 \(O(\frac{n}{B})\),差分复杂度为 \(O(nB)\),折中取 \(B=\sqrt{n}\),总复杂度即为 \(O(n\sqrt{n})\)

不过差分数组开 \(n\sqrt{n}\) 的空间是会 MLE 的,因此阈值也要向下调整一些,以及一些小的空间问题,建议前往讨论区。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=2e2+10;
int n,m;
int B;
int a[N];
int f[N][M];
int main(){
    cin>>n>>m;
    B=sqrt(n)/2;
    for(int i=1;i<=m;i++){
        int x,l,d;
        cin>>x>>l>>d;
        if(d<=B){
            f[x][d]++;
            f[x+l*d][d]--;
        }else{
            for(int j=0;j<l;j++){
                a[x+j*d]++;
            }
        }
    }
    for(int j=1;j<=B;j++){
        for(int i=1;i<=n;i++){
            if(i>=j) f[i][j]+=f[i-j][j];
            a[i]+=f[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        cout<<a[i]<<' ';
    }
    return 0;
}

posted @ 2025-09-25 20:43  幻琳  阅读(9)  评论(0)    收藏  举报