这里我的第一思路还是延续上一题对f的分段,即将f值相同的点作为一段,然后去计算对应段内的g值之和,然后对f值之和与g值求差的绝对值即可,这里需要注意的是可能存在f值<g值的情况,如果求和再相减会出现减多了的情况,实际上应该的操作是分别求差的绝对值,即实际上应该是加的,那么我们就需要补上减的数*2
#include<iostream> #include<cmath> #define LL long long using namespace std; int r,N,n; int A[100005]; int main(){ int num = 0; LL ans = 0; cin>>n>>N; r=floor((1.0 * N)/(1.0*(n+1))); for(int i=1;i<=n;i++){ cin>>A[i]; } A[n + 1] = N; for (int i = 1; i <= n+1; i++) { int sum_f = ((A[i] - A[i - 1]) * num++), sum_g = 0;//每一段的f值和 for (int j = A[i - 1]; j < A[i]; j++) { int temp = g(j); sum_g += temp;//每一段的g值之和 if (temp > num - 1) { sum_f += (2 * (temp - num + 1));//补上减去的部分 } } ans += abs(sum_f - sum_g); } cout << ans << endl; return 0; }
这里的时间复杂度只足以拿到70分
CSP的一二题联系紧密,在第一题中实际上已经给出了暗示:分段
上述思路也的确使用了分段,但是还是不够,我们还要对g值进行分段
即上图例子中,[0,1]、[2,3]、[4,4]、[5,5]、[6,7]、[8,9]为段
因此我们需要在每一个f段内对g进行分段,实际上我们不难发现我们求得的r值就是每r个数的g值都是相等的,利用这一点我们就可以对g值分段了
for(int j=a[i-1];j<=a[i]-1;j=j+Long){//此区间内有Long个g取值为g(j)的数
j值改变条件为:j=j+Long,这个Long为变值,因为==g取值相同的区间不完全与f取值相同的区间对齐==
由于此特性,所以g取值的上界可能超出了此区间(按f取值相同来划分的区间)的上界
所以需要此语句:if(NumEnd>a[i]-1) NumEnd=a[i]-1;//上界超出范围,变为区间最上界
,来将此情况的bug修复。最后对每个区间的差值计算后叠加即可
#include<iostream> #include<cmath> #define LL long long using namespace std; int r, N, n,sizee; int A[100005]; int g(int x) { return floor((1.0 * x) / r); } int main() { int num = 0; LL ans = 0; cin >> n >> N; r = floor((1.0 * N) / (1.0 * (n + 1))); for (int i = 1; i <= n; i++) { cin >> A[i]; } A[n + 1] = N; for (int i = 1; i <= n + 1; i++) {//对f进行分段 int sum = 0; for (int j = A[i - 1]; j < A[i]; j += sizee) {//对g进行分段 int endIndex = (g(j) + 1) * r - 1;//当前g分段的最后一个数的下标 if (endIndex > A[i] - 1) { endIndex = A[i] - 1; } int numGj = endIndex - j + 1; LL f_g = abs(i - 1 - g(j)); sum += (f_g * numGj); sizee = numGj; } ans += sum; } cout << ans << endl; return 0; }