2335: [SCOI2011]飞镖
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 482 Solved: 152
[Submit][Status][Discuss]
Description
飞镖是在欧洲颇为流行的一项运动。它的镖盘上分为20个扇形区域,分别标有1到20的分值,每个区域中有单倍、双倍和三倍的区域,打中对应的区域会得到分值乘以倍数所对应的分数。例如打中18分里面的三倍区域,就会得到54分。另外,在镖盘的中央,还有“小红心”和“大红心”,分别是25分和50分。
通常的飞镖规则还有一条,那就是在最后一镖的时候,必须以双倍结束战斗,才算获胜。也就是说,当还剩12分的时候,必须打中双倍的6才算赢,而打中单倍的12或者三倍的4则不算。特别的,“大红心”也算双倍(双倍的25)。在这样的规则下,3镖能解决的最多分数是170分(两个三倍的20,最后用大红心结束)。
现在,lxhgww把原来的1到20分的分值变为了1到K分,同时把小红心的分数变为了M分(大红心是其双倍),现在lxhgww想知道能否在3镖内(可以不一定用满3镖)解决X分。同样的,最后一镖必须是双倍(包括大红心)。
Input
Output
一行,包括一个数字,表示这T组数据中,能够被解决的数据数目。
Sample Input
1 2 2 10 20
1 3 2 15 25
2 2 5 200 170
Sample Output
HINT
1<=T<=1000000,20<=K1,M1,X1,D1,D2,D3<=10^9
0<=A1,B1,C1,A2,B2,C2,A3,B3,c3<=10^9
首先我们抛开m不论。
不难发现一个性质:两次分别选择2*k,3*k,就可以凑出2*k+3*k以内除了2*k+3*k-1以外所有的数。证明起来很简单,要凑2*k+3*k-1,就必须得用2*(k+1)+3*(k-1),可是k+1超过了范围,不合法。
2*k+3*k-2,即2*(k-1)+3*k.
2*k+3*k-3,即2*k+3*(k-1).
2*k+3*k-4,即2*(k-1)+3*k.
2*k+3*k-5,即2*(k-1)+3*(k-1).
此后每5个数即为一次循环,都能够凑出来(1是特例,但可以直接用一次1*1得到,所以不用在意)
那么能凑出比2*k+3*k还大的数,就只能选3*a+3*b这种方式,这种方式能凑出来的数规律很显然,即为3的倍数,且小于等于3*k+3*k。
所以,对于任何一个数,用这两种方式凑都是最优的。
因此,我们将x-2*k,看是否可以用2*a+3*b来凑
并且找到x-2*k1,为k1<=k且x-2*k1为3的倍数,看是否可以用3*k+3*k来凑
那么现在加入m
总结一下,有m参与的共计有11种情况:(i表示选1-k中的数,乘的倍数不论,竖线后为最后一次,前两次操作的顺序随意)
①m i | i
②2m i | i
③m m | i
④m 2m | i
⑤2m 2m | i
⑥i i | 2m
⑦m i | 2m
⑧2m i | 2m
⑨m m | 2m
⑩m 2m | 2m
⑪2m 2m |2m
将m与2m看做同一种数,可以将1,2归为一类,记为A
3,4,5归为一类,记为B
6单独为一类,记为C
7,8为一类,记为D
9,10,11为一类,记为E
对于A类,只需要将x-m或2m,因为最后一次必为2*a,所以用2*a+3*b的方法判定即可(注意!这里x-m,x-2m不可为0!因为题目要求的是从1-k内的数中选,如果m==x就会出现不合法的局面)
对于B类,将x-2m(m+m)或3m(m+2m)或4m(2m+2m),看剩下的数是否为2的倍数且在2*k范围内
对于C类,将x-2m后,看能否用2*a+3*b,或者3*a+3*b的方法即可
对于D类 ,将x-3m或4m后,是否为1-k中某个数本身或2倍,3倍
对于E类,直接看x是否等于4m,5m,6m。
#include<cstdio> using namespace std; typedef long long ll; ll A[5],B[5],C[5],D[5],S[5]; ll K,M,X;int T,tot; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } bool check(){ ll kx=K; if(X-2*K<=K*3+K*2&&X-2*K!=K*3+K*2-1) return 1; while((X-2*kx)%3) kx--; if((X-2*kx)<=K*6) return 1; return 0; } bool checkA(ll x){ if(x<2) return 0; if(x<=K*3+K*2&&x!=K*3+K*2-1) return 1; return 0; } //慎用除法 bool checkB(ll x){ if(x<0) return 0; if(x%2==0&&x<=K*2) return 1; return 0; } bool checkC(ll x){ if(x<0) return 0; if(x<=K*3+K*2&&x!=K*3+K*2-1) return 1; if(x%3==0&&x<=K*6) return 1; return 0; } bool checkD(ll x){ if(x<0) return 0; if(x<=K) return 1; if(x%2==0&&x<=K*2) return 1; if(x%3==0&&x<=K*3) return 1; return 0; } int main(){ // freopen("sh.txt","r",stdin); T=read(); for(int i=1;i<=3;i++) A[i]=read(),B[i]=read(),C[i]=read(),D[i]=read(),S[i]=read(); K=S[1];M=S[2];X=S[3]; for(int i=1;i<=T;i++){ if(check()) tot++; else if(checkA(X-M)||checkA(X-2*M)) tot++; else if(checkB(X-2*M)||checkB(X-3*M)||checkB(X-4*M)) tot++; else if(checkC(X-2*M)) tot++; else if(checkD(X-3*M)||checkD(X-4*M)) tot++; else if(X==4*M||X==5*M||X==6*M) tot++; K=(K*K%D[1]*A[1]%D[1]+K*B[1]%D[1]+C[1]%D[1])%D[1]+20; M=(M*M%D[2]*A[2]%D[2]+M*B[2]%D[2]+C[2]%D[2])%D[2]+20; X=(X*X%D[3]*A[3]%D[3]+X*B[3]%D[3]+C[3]%D[3])%D[3]+20; } printf("%d\n",tot); return 0; }