The first line of the input contains an integer T(1≤T≤200) , the number of test cases.
Each of the next T lines contains an integer , N the length of the sequence.
If multiple solutions exist, any of them will be accepted.
2 2 5
1 2 1 2 4 8 16
题意
输入n,让你构造一个长度为n的整数序列,要求序列任意一端连续子序列和不能重复。也就是假如s[i]表示前i项和,那么如果有s[i] - s[j] = s[k] - s[l] 当且仅当 i - j = k - l.
并且要求数列的每个元素都要小于3*n+18。
分析:
这个题真的是一个数学神题,估计出题人也是数学科班出身,相当神的一个构造。看来题解吧:
令p为一个大于n的素数,令 x 为 (0,p)之间的任意一个整数。
令s[ ] 表示所构造序列的前缀和数组。
构造数组如下 s[ i ] = 2 * i * p + r[ i ], 且r[ i ] = ( i * ( i + 1 ) / 2 * x ) % p ;
那么如果有s[ i ] - s[ j ] = s[ k ] - s[ l ];
推断如下:
带入通项公式:
2 * i * p + r[ i ] - 2 * j * p + r[ j ] = 2 * k * p + r[ k ] - 2 * l * p + r[ l ]
2 * p * ( i - j ) + r[ i ] - r[ j ] = 2 * p * ( k - l ) + r[ k ] - r[ l ] ;
因为对于r 数组的任意一个元素都是大于等于0并且小于p,所以r[ i ] - r[ j ] , r[ k ] - r[ l ] 都是小于p的。那么两者相差值最大也达不到2*p。然而如果i - j 不等于 k - l 那么至少相差1.那么等式两边至少相差2*p并且不能通过r数组平衡,所以必然有 i - j = k - l。①
也就是 r[ i ] - r[ j ] = r[ k ] - r[ l ] ;
化简:
( i * ( i + 1 ) / 2 * x ) % p - ( j * ( j + 1 ) / 2 * x ) % p = ( k * ( k + 1 ) / 2 * x ) % p - ( l * ( l + 1 ) / 2 * x ) % p
因为x<p 所以 x%p = x,则:
( i * ( i + 1 ) / 2 ) %p - ( j * ( j + 1 ) / 2 ) % p - ( k * ( k + 1 ) / 2 ) % p + ( l * ( l + 1 ) / 2 ) % p = 0
因为%p相当于减去 p的某个整数倍数。所以有:
( i * ( i + 1 ) / 2 ) - ( j * ( j + 1 ) / 2 ) - ( k * ( k + 1 ) / 2 ) + ( l * ( l + 1 ) / 2 ) = m*p; m为一个整数。
继续化简:
i * i + i - j * j + j - k * k + k + l * l + l = 2*m*p; m为一个整数。
因为 i - j = k - l .并且令 i - j = y. 所以可以化简为:
( j * 2 + y ) * y - ( l * 2 + y ) * y = 2*m*p
2 * j * y - 2 * l * y = 2*m*p
( j - l ) * y = m * p
因为( j - l ) < p, y < p 并且p为素数。所以一定只有m = 0;又因为 i != j 所以 有 j = l ②
根据①②可以推出来: i = k , j = l .则满足题意。
解法:
则对于一个输入的n,我们只需要找到一个比n大的最小素数p。然后在(0,p)中枚举x。根据公式求出s[i] .然后使得序列中每个数都小于3*n+18.
注意:
由于需要根据实际情况n来决定p和x,所以不能预处理出s[i] 数组。
在官方题解中x 的范围为 [ 0 , p ) 。然而显然 x 不能为0,否则求出来的数列一定是2*p的常数列。
代码:
#include<stdio.h>
int book[10000] ={1,1,0};
int s[2001];
void init()
{
for(int i = 2 ; i < 10000 ; i ++)
{
if(book[i])continue;
for(int j = 2 ; j * i < 10000 ; j ++)
book[i*j]=1;
}
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int p = n+1 ; p < 10000 ; p ++)
{
if(book[p]) continue;
for(int x = 1 ; x < p ; x ++)
{
int flag = 1;
for(int i = 1 ; i <= n ; i ++)
{
s[i] = 2*i*p + (i*(i+1)/2*x)%p;
if((s[i] - s[i-1]) >= 3*n + 18 )
{
flag = 0;
break;
}
}
if(flag)
{
break;
}
}
break;
}
for(int i = 1 ; i < n ; i ++)
printf("%d ",s[i]-s[i-1]);
printf("%d\n",s[n]-s[n-1]);
}
return 0;
}