你的名字叫czy是吧
(mynameisczy.pas/.c/.cpp)
尽管czy放了那么多只NTR酋长,也没能拦住黄巨大。黄巨大和czy相遇了……
“你的名字叫czy是吧”
“……”
“我们来单挑吧”
“……”
Hzw和czy决定用投骰子单挑。这骰子有6面,分别是1、1、1、2、2、2。如果投中1就是黄巨大赢一局,如果投中2就是czy赢一局。如果比对方多赢两局就获胜了。很公平吧?但是猥琐的czy捏碎了一张魔法卷轴,使得他可以把骰子投到2的概率改为p/q的形式(p,q为整数),但是他也不能确定这魔法卷轴改完概率是对他有益还是对黄巨大有益。所以他想知道在Q轮过后刚好比赛结束的概率是多少。但他知道最后的精确答案即既约分数a/b中a、b肯定很大,所以他只要知道a%k和b%k的数的大小就可以了,但是是精确的a%k和b%k。但是为了不让黄巨大瞄到他在干什么,czy要对数据进行加密……具体来说,第i个询问Qi的真正大小将是给定的Q减去上一个询问的a%k值,至于b%k就不管了。。。
输入
第一行四个数p,q,T,k,表示初始概率是p/q,以下T组询问,答案对k取模
接下来T行,每行一个数 Q,表示czy想知道比赛刚好在第Q轮停止的概率(已加密)
输出
T行,每行两个整数,表示要求的概率a/b中a%k和b%k的精确值。如果这个概率是0或1,直接输出0 0或1 1(中间有空格)。
样例输入1:
2 3 2 100
1
2
样例输出1:
0 0
5 9
样例输入2:
2 3 2 20
4
6
样例输出2:
0 1
0 9
数据范围:
对于30%数据,p,q<=5,T<=1000,k<=127,对于任意解密后的Q,有Q<=30
对于60%数据,p,q<=20,T<=100000,k<=65535,对于任意解密后的Q,有Q<=1000
对于100%数据,p,q<=100,T<=1000000, k<=2147483647,对于任意解密后的Q,有Q<=1000000
对于100%数据,有q>p,即0<= p/q<=1,但是不保证给定的p/q就是既约分数
zhb原创出品,改编自高一暑假数学作业必修三那章最后一题
这是这套题唯一会比较防ak的题了
首先题目我写了一大堆,就是要把你搞晕的
题意是有两个人进行游戏,其中第一个人在每局中获胜的概率是p/q,如果有一个人比另一个人多赢两局,则游戏结束。现在给出T个询问,每个询问Q表示求游戏刚好在第Q轮结束的精确概率a/b的a%k和b%k。要求a/b是这个概率的最简分数。
解法是这样的:
我们把每两局压成一轮,只有三种可能:第一个人赢了,第二个人赢了,两人各赢一局。这样如果有人赢了游戏结束,平局时两人分数相同,相当于又开始一局
这样我们注意到一个显然的事实:游戏不可能在奇数局结束。因为由上面的推论+自己yy可知,要结束一定是在一轮以后,就是偶数局之后。这样不合法情况删掉一半了
第一个人一轮赢必须连赢两局,就是(p/q)^2,即p^2/q^2
第二个人一轮赢也是连赢两局,就是(1-p/q)^2,通分完(p-q)^2/q^2
那么一局能结束的概率就是上面两个加起来,即[p^2+(p-q)^2]/q^2
一局不能结束的概率就是1-"上面那式子"
为简化条件,我们令一轮能结束的概率是A/B,一轮不能结束的概率是C/D。计算方法见上
那么对于有意义的询问,即偶数Q,令t=Q/2
那么比赛在第t轮即第Q局结束的充要条件是:在1到t-1轮两人都是平局,并且在第t轮比赛刚好结束
那么对于询问Q,[(C/D)^(t-1)] * (A/B) 即是所求
到这里应该都还能理解吧
然后比较难搞的是取模。因为p、q是100级别,那么p^2、q^2是1w级别,就是说ABCD这些数都是10000级别
要求的分数分子是C^(t-1)*A,分母是D^(t-1)*B,还要进行约分完取模。我们可以直接预处理使得A和B、C和D分别互质,但是我们没法保证A和D、B和C分别互质。这样约分就有困难了。比如A分解质因数有2^十几次方吧,D只有2^1,那么在接下来的十几次操作中都要用D的2去约掉A的2。但是ABCD的数据规模还算小,所以我们暴力搞出前20轮的答案,然后这样一来改约的也就约干净了,然后每次分子只乘C分母只乘D,又没有约分,可以直接递推。
#include<cstdio> #define MX 10000 //ABCD的规模是10000 #define primeMX 1230 //10000以内1229个质数 #define LL long long int prime[primeMX]; struct fenjie{ int rep[primeMX]; }aa,bb,cc,dd; int p,q,T,k,a,b,c,d; int ss[primeMX]; //用于暴力,分解质因数之后直接加/减在上面,如果是正的表示分子的分解质因数有ss[i]个prime[i],反之分母亦然 LL Q,last; LL ansa[1000010],ansb[1000010]; //保存第i局结束的概率的分子分母 inline void shai() //筛法 { bool mrk[10010]={0}; int leng=0; for (int i=2;i<=MX;i++) if (!mrk[i]) { for (int j=2*i;j<=MX;j+=i)mrk[j]=1; prime[++leng]=i; } } inline int gcd(int a,int b) //gcd用于A和B、C和D先约分 {if (!b)return a;else return gcd(b,a%b);} inline void divide(fenjie &a,int b) //分解质因数,用于前20轮暴力用 { for (int i=1;i<primeMX;i++) { if (b==1)break; while (b%prime[i]==0){b/=prime[i];a.rep[i]++;} } } inline LL read() //快速读入,100w的询问不加肯定TLE { 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; } int main() { freopen("mynameisczy.in","r",stdin); freopen("mynameisczy.out","w",stdout); shai(); p=read();q=read();T=read();k=read(); if (p==1&&q==1||p==0&&q==0)ansa[2]=ansb[2]=1; //如果必胜或必败,那一定在第2局就结束。其余概率都是0 else { c=p*p+(q-p)*(q-p); b=q*q; a=b-c; d=b; //这里我AB和CD的意义反过来了 int div1=gcd(a,b);a/=div1;b/=div1; int div2=gcd(c,d);c/=div2;d/=div2; divide(aa,a); divide(bb,b); divide(cc,c); divide(dd,d); ansa[2]=c%k;ansb[2]=d%k; for (int i=1;i<primeMX;i++) ss[i]+=cc.rep[i]-dd.rep[i]; for(int i=4;i<=40;i+=2) { long long sum1=1,sum2=1; for (int j=1;j<primeMX;j++) ss[j]+=aa.rep[j]-bb.rep[j]; for (int j=1;j<primeMX;j++) if (ss[j]>0) for (int l=1;l<=ss[j];l++)sum1=(sum1*prime[j])%k; else if (ss[j]<0)for (int l=1;l<=-ss[j];l++)sum2=(sum2*prime[j])%k; ansa[i]=sum1;ansb[i]=sum2; } for (int i=42;i<=1000000;i+=2) { ansa[i]=(ansa[i-2]*a)%k; ansb[i]=(ansb[i-2]*b)%k; } } for(int i=1;i<=T;i++) { Q=read()-last; //last是处理加密的问题 last=ansa[Q]; printf("%I64d %I64d ",ansa[Q],ansb[Q]); } return 0; }