一些做题得到的东西
1.非递归的归并排序
我们可以用于思考一些题,
如果它可以等于左边部分加上右边部分加上跨左右的部分
如果可以优化成归并排序的方法,你可以尝试使用这个方法
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int help[1008611];
int arr[1008611];
void merge(int l,int m,int r){
int i=l;
int a=l;
int b=m+1;
while(a<=m&&b<=r){
help[i++]=arr[a]<=arr[b]?arr[a++]:arr[b++];
}
while(a<=m){
help[i++]=arr[a++];
}
while(b<=r){
help[i++]=arr[b++];
}
for (int j = l; j <=r ; ++j) {
arr[j]=help[j];
}
}
void solve(){
for(int l,m,r,step=1;step<n;step<<=1){
l=0;
while(l<n){
m=l+step-1;
if(m+1>=n){
break;
}
r=min(l+(step<<1)-1,n-1);
merge(l,m,r);
l= r+1;
}
}
}
int32_t main() {
cin>>n;
for (int i = 0; i <n ; ++i) {
cin>>arr[i];
}
solve();
for (int i = 0; i < n; ++i) {
cout<<arr[i];
}
return 0;
}
2.同余定理
题目K倍区间
如果两个前缀和除以k得到相同的余数,那么这两个前缀和之差一定是k的倍数
数学表达: 如果 sum[i] % k = sum[j] % k,那么 (sum[i] - sum[j]) % k = 0
举例
假设有序列 [3, 1, 4, 2],k = 3
前缀和序列为: [0, 3, 4, 8, 10]
各个前缀和对3取余: [0, 0, 1, 2, 1]
余数为0的位置: 0, 1
余数为1的位置: 2, 4
余数为2的位置: 3
因此可以构成的k倍区间数量 = C(2,2) + C(2,2) + C(1,2) = 1 + 1 + 0 = 2
详细解释一下这个组合计算过程:
当我们找到前缀和对k取余后相同的位置时,这些位置两两之间的区间和都是k的倍数。我们用组合数来计算这些位置能形成多少个区间。
在这个例子中:
code
序列: [3, 1, 4, 2]
前缀和: [0, 3, 4, 8, 10]
余数: [0, 0, 1, 2, 1]
余数为0的位置有两个:位置0和位置1
C(2,2) 表示从这2个位置中选择2个位置的组合数
C(2,2) = 1,即可以形成1个区间:(0,1)
余数为1的位置有两个:位置2和位置4
C(2,2) = 1,即可以形成1个区间:(2,4)
余数为2的位置只有一个:位置3
C(1,2) = 0,因为只有一个位置,无法形成区间
总的区间数 = 1 + 1 + 0 = 2
#include<iostream>
#include<string.h>
using namespace std;
int sett[100002];
long gcc[100002];
int main()
{
int n,k;
cin>>n>>k;
long count=0;
long sum=0;
memset(gcc,0,sizeof(gcc));
for(int i=0;i<n;i++)
{
cin>>sett[i];
sum+=sett[i];//记录前缀和
gcc[ sum%k ]++;//余数相同的区间
}
//余数相同的区间相减可以构成一个k倍区间
count = gcc[0];
for(int i=0;i<k;i++)
{
count+=gcc[i]*(gcc[i]-1)/2;
}
cout<<count<<endl;
return 0;
}