HDU-6874 Decision 倍增 (2020 HDU多校 D7 D)
Decision
题意
从 中等概率的选取两个数字 , 定义序列 有 。如果 是偶数,则获胜,求获胜概率
范围:
分析
枚举 的值,考虑 的可能取值。
先考虑 的情况
- 若 是偶数,那么 可能取值为 , 并且0只会被取到一次,其他偶数会取到两次(为什么会取到两次?考虑 与 对称取值)。
- 若 是奇数,那么 可能取值为 , 所有数字都会取到两次
然后 的情况类似
- 若 是偶数,那么 可能取值为 , 并且0只会被取到一次,其他偶数会取到两次。
- 若 是奇数,那么 可能取值为 , 所有数字都会取到两次
现在的问题是如何快速根据, 求出所有值的贡献,注意到 , 同组 都只会隔2递增,我们可以预处理出每个sum往后跳两次的位置在哪里(跳是指 ), 然后倍增处理即可。
const int N = 1000000 + 5; int T, t, a, c, m; int nxt[N], tt; int f[N][21], d[N][21]; ll res; void get(int x, int p){ for(int i=tt;i>=0;i--){ if(p >= (1 << i)){ res += 2*d[x][i]; // 每个dis的贡献都是二倍 p -= 1 << i; x = f[x][i]; } } } ll gcd(ll a, ll b){return b == 0 ? a : gcd(b, a%b);} int main(){ scanf("%d", &T); while(T--){ scanf("%d%d%d%d",&t, &a, &c, &m); for(int i=0;i<m;i++){ nxt[i] = (1ll * a * i + c) % m; //nxt[i] 表示 i 往后跳一次的位置 } for(int i=0;i<m;i++){ f[i][0] = nxt[nxt[i]]; // f[i][0] 表示 i 往后跳2^i次的位置 // d 数组计算贡献,当坐标为偶数时贡献为 1,注意这里并没有计算起点 i 的贡献,后面需要单独计算 d[i][0] = f[i][0] % 2 == 0; } tt = log2(2*t-1) + 1; for(int j=1;j<=tt;j++) { for(int i=0;i<m;i++){ f[i][j] = f[f[i][j-1]][j-1]; d[i][j] = d[i][j-1] + d[f[i][j-1]][j-1]; } } res = 0; // 枚举 sum for(int i=0;i<=2*t;i++) { if(i <= t) { if(i % 2 == 0) { res ++; // sum 为偶数,dis 为 0 时有一次贡献 get(i, i / 2); // 计算之后的贡献,跳跃次数为 sum / 2 } else { // sum 为奇数,单独计算 dis 为 1 时的贡献 if(nxt[i] % 2 == 0) res += 2; // 从 nxt[i] 开始,计算剩下的贡献 get(nxt[i], (i - 1) / 2); } } else { if(i % 2 == 0) { res ++; get(i, (2 * t - i)/2); } else { if(nxt[i] % 2 == 0) res += 2; get(nxt[i], (2 * t - i - 1) / 2); } } } ll all = 1ll*(t + 1) * (t + 1); ll g = gcd(all, res); printf("%lld/%lld\n", res/g, all/g); } return 0; }
其他: 场上并没有想出这道题,列出X关于sum和dis的通项之后手足无措,sum和dis奇偶性保持一致,sum确定后,dis是等间隔取到的,但是仍然没有想到可以用倍增处理。
注:转载请注明出处
本文作者:kpole
本文链接:https://www.cnblogs.com/1625--H/p/13491037.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
标签:
思维
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步