倍增法+二分 hnu13547 Lily'game
来源:互联网 发布:医疗软件销售怎么样 编辑:程序博客网 时间:2024/05/29 14:33
传送门:点击打开链接
题意:给了一个原串A,和一个排列B。每次操作,把串A按照排列B去变换,然后把奇数位的数字全部乘以2。问把原串A经过很多次操作以后,能否得到c*2^d。如果能得到,就输出最小操作次数,否则输出-1
思路:我们来分析一下,首先对于按排列变换的问题,当然第一步是把变换转换成有向图模型,即连一条边i->B[i]。我们能发现会得到很多个环。
不同的环答案是独立的。我们对每个环经行考虑。假如A[i]变换以后能变成c*2^d,乘以了x个2,那么就说明从位置i按有向图行走,恰好会经过x个点,这些点的编号是奇数。
我们用倍增法去处理从一个点u出发,向下的第1个点,第2个点,第4个点...第2^k个点的位置,并保存路径上奇数编号点的个数。
如果整个环上有y个奇数点,我们需要x个,我们先用x%y,简化一下,那么在最后一圈的时候,我们再去二分答案。
最后的总复杂度是O(n(logn)^2),应该是能做到O(nlogn)的,只不过我倍增法写的比较搓,多赠送一个logn
但是这个题的trick点实在太多,导致比赛的时候一直卡- -
1.答案会爆int
2.如果环上的奇数点个数为0,要特判一下,不然会整除0,就会RE
3.要先把c中的2除掉,并加到d中去
4.在x%y的时候,也要格外注意整除的时候,整除时步数有可能不是走了完整的x/y个环,步数可能更少。
#include <map>#include <set>#include <cmath>#include <ctime>#include <stack>#include <queue>#include <cstdio>#include <cctype>#include <bitset>#include <string>#include <vector>#include <cstring>#include <iostream>#include <algorithm>#include <functional>#define fuck(x) cout<<"["<<x<<"]";#define FIN freopen("input.txt","r",stdin);#define FOUT freopen("output.txt","w+",stdout);using namespace std;typedef long long LL;typedef pair<int, int> PII;const int MX = 1e5 + 5;const LL INF = 0x3f3f3f3f;int n;int z[MX], A[MX], nlen;int nxt[MX][20], val[MX][20];int belong[MX], clen[MX], cval[MX], bcnt;bool vis[MX];void BFS(int u, int dfn) { queue<int> Q; Q.push(u); while(!Q.empty()) { u = Q.front(); Q.pop(); clen[dfn]++; vis[u] = 1; if(u & 1) cval[dfn]++; belong[u] = dfn; nxt[u][0] = z[u]; val[u][0] = z[u] % 2; if(!vis[z[u]]) { vis[z[u]] = 1; Q.push(z[u]); } }}void presolve() { bcnt = 0; memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i++) { if(!vis[i]) { ++bcnt; clen[bcnt] = cval[bcnt] = 0; BFS(i, bcnt); } } for(int i = 1; i <= nlen; i++) { for(int j = 1; j <= n; j++) { nxt[j][i] = nxt[nxt[j][i - 1]][i - 1]; val[j][i] = val[j][i - 1] + val[nxt[j][i - 1]][i - 1]; } }}int get(int m, int u) { int ret = 0; for(int i = 0; i <= nlen; i++) { if(m >> i & 1) { ret += val[u][i]; u = nxt[u][i]; } } return ret;}LL solve(int u, int d) { int block = belong[u]; if(d == 0) return 0; if(cval[block] == 0) return -1; int k = (d - 1) / cval[block]; LL ret = (LL)k * clen[block]; d -= k * cval[block]; int l = 0, r = clen[block], m; while(l <= r) { m = (l + r) >> 1; int temp = get(m, u); if(temp >= d) r = m - 1; else l = m + 1; } return ret + r + 1;}int getmax(int n) { for(int i = 19; i >= 0; i--) { if(n >> i & 1) return i; }}int main() { //FIN; while(~scanf("%d", &n)) { for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); } for(int i = 1; i <= n; i++) { scanf("%d", &z[i]); } nlen = getmax(n); presolve(); int c, d; LL ans = -1; scanf("%d%d", &c, &d); while(c % 2 == 0) d++, c /= 2; for(int i = 1; i <= n; i++) { if(A[i] % c != 0) continue; A[i] /= c; int s = 0; while(A[i] % 2 == 0) A[i] /= 2, s++; if(A[i] != 1 || s > d) continue; LL ret = solve(i, d - s); if(ret != -1) { if(ans == -1) ans = ret; else ans = min(ans, ret); } } printf("%d\n", ans); } return 0;}
0 0
- 倍增法+二分 hnu13547 Lily'game
- 二分新姿势--倍增法
- Lily
- [HDU 5726] GCD (倍增法+二分)
- 【运输计划】【二分】【树上倍增】
- [练习][bzoj2783]二分+倍增 树
- 倍增法
- BZOJ 2783 JLOI 2012 树 倍增+二分
- NOIP2012 疫情控制(二分,倍增,贪心)
- noip2012 疫情控制 (二分+倍增)
- hihoCoder1232 Couple Trees LCA倍增算法+二分
- 疫情控制(二分+贪心+倍增)
- 【noip2015】【Day2】【二分】【倍增】运输计划
- 玲珑杯-1112 (倍增+二分)
- 【玲珑杯 Round#13 B】 【倍增+二分】
- HDU4343[Interval query]--倍增思想+二分+离散
- HDU6031[Innumerable Ancestor]--二分+倍增思想
- BZOJ 2783 JLOI2012 树 二分 + 倍增
- 例说多线程定时器System.Timers.Timer
- 新手福利——理解java多态的概念和作用
- 相册磁盘缓存分析
- iOS尺寸单位pt、ppi与px之间换算关系
- Hbase安装详解
- 倍增法+二分 hnu13547 Lily'game
- 二分图判定
- Android自定义视图四:定制onMeasure强制显示为方形
- <html>头部元素
- DoTween部分源码
- Linux 下 Git 的源码安装
- 前后端分离(二)
- <html>符号
- springMVC工作原理及源码分析