题目描述
Mz们在czy的生日送他一个黑红树种子……czy种下种子,结果种子很快就长得飞快,它的枝干伸入空中看不见了……
Czy发现黑红树具有一些独特的性质。
这是二叉树,除根节点外每个节点都有红与黑之间的一种颜色。
每个节点的两个儿子节点都被染成恰好一个红色一个黑色。
这棵树你是望不到头的(树的深度可以到无限大)
- 黑红树上的高度这样定义
(根节点)=0,h[son]=h[father]+1。
Czy想从树根顺着树往上爬。他有p/q的概率到达红色的儿子节点,有1-p/q的概率到达黑色节点。但是他知道如果自己经过的路径是不平衡的,他会马上摔下来。一条红黑树上的链是不平衡的,当且仅当红色节点与黑色节点的个数之差大于1。现在他想知道他刚好在高度为h的地方摔下来的概率的精确值a/b,gcd(a,b)=0。那可能很大,所以他只要知道a,b对K取模的结果就可以了。另外,czy对输入数据加密:第i个询问Qi真正大小将是给定的Q减上一个询问的第一个值a%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
样例输入2
2 3 2 20
4
6
样例输出
样例输出1
0 0
5 9
样例输出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<=1000000007,对于任意解密后的Q,有Q<=1000000
对于100%数据,有q>p,即0<= p/q<=1
题解
不写了
主要就是两行两行的考虑
#include <cstdio>
#include <cstring>
int P;
class frac
{
public:
long long a, b;
long long gcd(long long A, long long B)
{
return B == 0 ? A : gcd(B, A % B);
}
frac Update(frac s)
{
if (s.a == 0)
return s;
int GCD = s.gcd(s.a, s.b);
return (frac){s.a / GCD, s.b / GCD};
}
frac operator*(const frac A)
{
return (frac){a * A.a % P, b * A.b % P};
}
frac operator*(const int A)
{
return (frac){a * A % P, b % P};
}
};
frac ans[1000001];
// 0 b, 1 r
// 0 = ,1 b>r ,2,b<r
int main()
{
//freopen("brtree.in", "r", stdin);
//freopen("brtree.out", "w", stdout);
int p, q, T;
scanf("%d%d%d%d", &p, &q, &T, &P);
p %= P;
q %= P;
frac B = (frac){q - p, q};
frac R = (frac){p, q};
frac BR = B * R * 2;
frac BBRR = (frac){p * p + (q - p) * (q - p), q * q};
BR = BR.Update(BR);
BBRR = BBRR.Update(BBRR);
ans[2] = BBRR;
for (int i = 4; i <= 1000000; i += 2)
{
ans[i] = ans[i - 2] * BR;
}
int k = 0, r = 0;
while (T--)
{
scanf("%d", &k);
k -= r;
//int Gcd = ans[k].gcd(ans[k].a, ans[k].b);
r = 0;
printf("%lld %lld\n", ans[k].a % P, ans[k].b % P);
r = ans[k].a % P;
}
}