bzoj 3119 book
题意:给你一个N项数列的首项X,之后的每一项要么比前一项多A,要么少B,N项前缀和为M,构造一个可行的数列。
分析:首先,后一项只和前一项有关,那么对于一项来说+A和-B都是对后面每一项都造成影响的,比如第i个位置选择+A,那么后面N-i+1个位置相对都+A。于是我们设一共加了x个A,y个b,容易得到:
ax+by=M-N*X
x+y=n*(n-1)/2
这样我们可以解得x和y,那么怎么求方案呢?
我们看到x和y是由1..n-1这些数组合而成的。并且我们发现,1..n*(n-1)/2中所有的数都可以用1..n-1中得数组合成,我们观察组合的过程(以10为例),1=1,2=2,3=3...11=10+1,12=10+2...20=10+9+1,21=10+9+2...28=10+9+8+1,29=10+9+8+2...45=10+9+8+7+6+5+4+3+2+1,我们看到这些数的组合规律:从后向前贪心地选取大数,能选则选,到最后一定可以组合成要求的数。
于是我们就可以这样把组成x的数求出来,从而得到每一个位置是+A还是-B了。
贴个代码:
book
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 using namespace std; 7 long long N,M,n,m,x,y,X,A,B; 8 bool v[200000]; 9 10 int main() 11 { 12 scanf("%lld",&N); 13 scanf("%lld%lld%lld%lld",&X,&A,&B,&M); 14 m=M-N*X; 15 n=N*(N-1)/2; 16 x=(m+B*n)/(A+B); 17 y=n-x; 18 int now=N-1; 19 while (x) 20 { 21 if (x-now>=0) 22 { 23 v[now]=1; 24 x-=now; 25 } 26 now--; 27 } 28 printf("%lld",X); 29 for (int i=N-1;i>=1;i--) 30 { 31 if (v[i]) X+=A; else X-=B; 32 printf(" %lld",X); 33 } 34 printf("\n"); 35 return 0; 36 } 37
AC without art, no better than WA !