整除分块

题目
https://ac.nowcoder.com/acm/contest/11160/C

1.莽环节(完全没有干货)

一开始啥都不会只能自己瞎做,瞎做思路:

这是100/i (i属于[1,100])的打表

100 50 33 25 20 16 14 12 11 10 9 8 7 7 6 6 5 5 5 5 4 4 4 4 4 3 3 3 3 3
3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

总结规律发现 count 1 = 100-50, count 2 = 50-33, count 3 = 33-25, count i = list[i-1]-list[i] 直到整个表别遍历完一次

按照这个思路 遍历表,从左右两个方向累加求和
如:

第一轮: sum+= 100*1(1被使用的次数 * 1的值)+ sum(51-100)(出现次数相同的一批数的求和 * 这些数是出现次数)


第二轮:sum+= 50*2 (2被使用的次数 * 2的值)+sum(34-50)(出现次数相同的一批数的求和 * 这些数是出现次数)


直到遍历完整个表得到答案

代码,时间7ms,内存508K

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
 
typedef long long LL;
 
int cnt[50000];
 
int main()
{
   LL n;
   std::cin >> n;
   if(n == 1)
   {
      std::cout << 1;
      return 0;
   }
   //n=5e4;
   LL sum = 0,rec = 0,nD2 = n/2,num,top;
   bool judge = false;
   for(int i=1; i<=nD2; i++)
   {
      rec += (n/i)*i;
   }
   rec += (nD2+1+n)*(n-nD2)/2;
   cnt[0] = rec;
   cnt[1] = rec/2;
   top = rec;
   int i;
   for(i=1; i<=cnt[i]; i++)
   {
      cnt[i+1] = rec/(i+2);
      num = cnt[i-1]-cnt[i];
      sum += (rec/i)*i;
      sum += (top+top-num+1)*num*i/2;
      if (i == cnt[i]) judge = true;
      top -= num;
   }
   if(!judge)
   {
      sum+= (rec/i)*i;
   }
   std::cout << sum ;
   return 0;
}
2.整除分块

这是100/i (i属于[1,100])的打表

100 50 33 25 20 16 14 12 11 10 9 8 7 7 6 6 5 5 5 5 4 4 4 4 4 3 3 3 3 3
3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

我们发现有一些相同的数 且 每一块的最右边的数的位置满足 right (100/i) = 100/(100/i) (从1开始计数)
发现1中的个数问题完全可以用整除分块解决, 直接分块求和就可以
代码,时间5ms,内存376K、

#include <iostream>
typedef long long LL;
LL g(LL n)
{
   LL sum = 0;
   for(LL l=1,r; l<=n; l=r+1)
   {
      r = n/(n/l);
      sum += (l+r)*(r-l+1)*(n/l)/2;
   }
   return sum;
}
int main()
{
   LL n;
   std::cin >> n;
   std::cout << g(g(n));
   return 0;
}

3.其他

抄一个时间2ms的神仙做法,大概有1的思想,正在研究
代码,时间2ms,内存500K

#include <stdio.h>
typedef long long LL;

LL g(LL n){
	LL ret = 0;
	for(LL i=1;i*i<=n;i++){
		LL cnt = n/i-i;
		ret += (cnt+1)*i;
		ret += (i+1+n/i)*cnt/2;
	}
	return ret;
}

int main(){
	int n;
	scanf("%d",&n);
	printf("%lld\n",g(g(n)));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值