1230. K倍区间(前缀和)

题目:

1230. K倍区间 - AcWing题库

1230. K倍区间(前缀和)_第1张图片

突破口:

区间遍历枚举一般先枚举右端点,再枚举左端点,注意由右端点限制左端点

思路:1.暴力

#include
#include
#include
#include
using namespace std;
typedef unsigned long long ull;
const int N = 100010;
ull s[N];//既是用来存储原数据的,也是用来存储前缀和的
ull n, k,ans;

/*区间遍历枚举一般先枚举右端点,再枚举左端点,注意由右端点限制左端点*/

int main()
{
    cin >> n >> k;
    
    //法一:暴力(时间复杂度:O(n3))
    for (int i = 1; i <= n; i++) scanf("%d", &s[i]);
    

    for(int r=1;r<=n;r++)
        for (int l = 1; l <= r; l++) {
            int sum = 0;
            for (int i = l; i <= r; i++)sum += s[i];
            if (sum % k == 0)ans++;
        }
    cout << ans;
}

思路二:前缀和

#include
using namespace std;
typedef unsigned long long ull;
const int N = 100010;
ull s[N];//既是用来存储原数据的,也是用来存储前缀和的
ull n, k,ans;

/*区间遍历枚举一般先枚举右端点,再枚举左端点,注意由右端点限制左端点*/

int main()
{
    cin >> n >> k;
    //法二:前缀和(时间复杂度:O(n2))
    for (int i = 1; i <= n; i++) {
        scanf("%d", &s[i]);
        s[i] += s[i - 1];//前缀和
    }

    for (int r = 1; r <= n; r++) {
        for (int l = 1; l <= r; l++) {
            if ((s[r] - s[l - 1]) % k == 0)ans++;
        }
    }
    cout << ans;
}

思路三:在前缀和基础上优化

#include
#include
#include
#include
using namespace std;
typedef unsigned long long ull;
const int N = 100010;
ull s[N];//既是用来存储原数据的,也是用来存储前缀和的
ull n, k,ans;
ull cnt[N]; // 用云存储余数为s[i] % k的数有多少个
/*区间遍历枚举一般先枚举右端点,再枚举左端点,注意由右端点限制左端点*/

int main()
{
    cin >> n >> k;

    /*法三:前缀和优化(数论)(时间复杂度:O(n))----以空间换时间
           对于for (int l = 1; l <= r; l++) 
                if ((s[r] - s[l - 1]) % k == 0)ans++;
           这是求在0~r-1的范围内有多少个s[l]可以满足(s[r] - s[l - 1]) % k == 0-->
           其等价于求在右端点r之前余数为s[r]%k的数有多少个
           定义cnt[N],用云存储余数为s[i]%k的数有多少个*/
    
    for (int i = 1; i <= n; i++) {
        scanf("%d", &s[i]);
        s[i] += s[i - 1];//前缀和
    }

    for (int r = 0; r <= n; r++) {
        ans += cnt[s[r] % k];
        cnt[s[r] % k]++;
    }
    cout << ans;
}

你可能感兴趣的:(前缀和,c++,算法,开发语言)