BZOJ 3119 Book (贪心+数学推导)

来源:互联网 发布:淘宝商家进货渠道 编辑:程序博客网 时间:2024/06/04 18:50

URL: http://www.lydsy.com/JudgeOnline/problem.php?id=3119

题目大意:
给定一个序列v[]的长度n (n<=1e5), 第一个元素的值p以及序列中所有元素的和m (m在long long范围内), 规定对于任意的2<=i<=n, 都有v[i]=v[i-1]+a或v[i]=v[i-1]-b, 同时给定a,b, 确定一组可能的v[]至并输出。

思路分析:
为了方便起见,先将b变为输入数的相反数(即变为一个负数)。
首先,考虑初始值x造成的影响: 它使得1至n的序列所有元素增加了x, 因此可以将这个值剔除,这样和变成了mnp.
其次,考虑第i个元素的值v[i]=v[i-1]+a造成的影响: 它使得i至n的序列中所有元素增加了a,使总和增加了(n+1i)a.
同理可得,若v[i]=v[i-1]+b (b为负数)则使总和增加了(n+1i)b
在这里,我们称这次赋值对总和产生了(n+1i)点影响。
显然,所有的赋值对总和一共产生了ni=2(n+1i)=n1i=1i=12n(n1)点影响。设其中+a产生的影响为x点,+b产生的影响为y点。由刚才的结论得:

x+y=12n(n1)
xa+yb=mnx

解二元一次方程组可得
x=12n(n1)y
y=12an(n1)m+npab

于是只需构造方案即可。这一步比较简单。可以用贪心来实现。
我们的目标是在1至n得正整数范围内找到一些互不相同数使得他们的和恰好是x,其余的数的和恰好是y, 因为x+y=12n(n1).因此,我们只需选出和为x的部分。考虑到
1=1
2=2,3=2+1
4=3+1,5=3+2,6=3+2+1
7=4+3,8=4+3+1,9=4+3+2,10=4+3+2+1
...

注:第i行能用的最大的数是i.
因此,我们得到一种贪心策略: 从n开始,从大到小依次选择,如果剩余的x能够选上i就选,并且x-=i, 直至x==0为止,剩余的就是y.
若为x, 则v[i]=v[i-1]+a; 否则v[i]=v[i-1]+b (b<0)
但由于对总和产生了i点影响的是(n+i-1)号决策,因此输出时勿忘倒着输出。

代码实现:
(Memory: 920KB; Time: 376MS; Code: 614B)

#include<cstdio>using namespace std;const int MAXN = 1e5;bool inc[MAXN+2];int main(){    long long a,b,x,y,m,n,p;    scanf("%lld%lld%lld%lld%lld",&n,&p,&a,&b,&m);    b = -b;    y = (a*n*(n-1)/2-m+n*p)/(a-b);    x = n*(n-1)/2-y;    long long cur = p;    for(long long i=n-1; i && x; i--)    {        if(x>=i)        {            inc[i] = true;            x-=i;        }    }    printf("%lld",cur);    for(long long i=n-1; i; i--)    {        if(inc[i]) cur+=a;        else cur+=b;        printf(" %lld",cur);    }    return 0;}
原创粉丝点击