数学推导______Different Sums( hdu 5847 2016多校第九场)

Problem Description
A subsum of the sequence is sum of one or more consecutive integers of it. You are given an integer  N ( 1N2000  ). Your task is to make a sequence of integers which are less than  3(N+6) , such that its all subsums ( N(N+1)/2  in total) are different from each other.
 

Input
There are several test cases.

The first line of the input contains an integer  T(1T200) , the number of test cases.

Each of the next  T  lines contains an integer , N  the length of the sequence.
 

Output
For each test case, print one line with  N  space separated integers representing your sequence.

If multiple solutions exist, any of them will be accepted.
 

Sample Input
  
  
2 2 5
 

Sample Output
  
  
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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值