首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/635/D
来源:牛客网
设计:等差数列
题目如下:
当时被暴力蒙蔽了双眼,没想到只要在excel找一下规律就可以了
先解释一下题目意思:
这里有一个环,环上面写着1->n的数,ddd从1开始每次走k步,直到第二次走到了结束(如果ddd在某次走k步时跨过了1,则还要继续走一圈),ddd每次的落脚点都有一个数,叫你升序输出k从1到n所以情况下,ddd从开始走到结束落脚点数字的和(卧槽,k好大,一个个遍历绝对超时)
首先我们可以得到一个结论,ddd落脚点的数字绝对不会大于n,而且结束之前不可能在同一个位置落脚两次。
我们假设这不是一个环,那么ddd所走的路线一定是一个等差数列a[n],但是现在ddd走的是一个环,也就是说,每次ddd落脚点的真实值其实是a[i] mod n,所有的值构成一个数列b[n](b[i]=a[i] mod n),既然a[n]是等差数列,那么b[n]又是一个怎样的数列?
根据以下表格(n=10):
和下列这个表格(n=6):
可以不完全归纳出一个事实,即:
每个b数列经过排序后得到的是一个首项为1,末项为n+1,公差为n的因数的数列(项数为n/k+1,k为n的因数)
那么可以把n的所有因数放在一个数组里得到一个公差数组,然后再通过公差数组得到每个公差得到对应的等差数列前n项和,得到的前n项和还要减去数列最后的n+1这个数,所有的前n项和升序输出即为答案!
至于如何得到n的所有因数,可以先把n开方,得到sqrt(n),再从1遍历到sqrt(n),只要n%i==0,那么i和n/i就是n的因数!
不过我用的是链表,方便增删,还有unique函数和sort函数可以用!
前n项的的公式减去
(
n
+
1
)
(n+1)
(n+1)应该是:
(
n
+
2
)
∗
(
n
/
k
+
1
)
/
2
−
n
−
1
(n+2)*(n/k+1)/2-n-1
(n+2)∗(n/k+1)/2−n−1
ps:数据类型很大,要用long long类型!!!
代码如下:
#include <iostream>
#include <list>
#define f(i) (ll)(n+2)*(n/i+1)/2-n-1//通过公差i得到前n项和减去(n+1)的值
using namespace std;
typedef long long ll;
int n;
list<ll> list1;//链表用来存所有的前n项和(减去n+1)
int main(){
cin>>n;
int i,j=1;
for(i=1;i*i<=n;i++)
if(n%i==0){//i为n因数的条件
list1.push_back(f(i));
list1.push_back(f((n/i)));//放入链表
}
list1.unique();list1.sort();
//unique函数用来删除重复的值,sort函数用来排序
while(list1.size()!=0){//输出
cout<<list1.front()<<" ";
list1.pop_front();
}
return 0;
}