标签:
传送门:点击打开链接
题意:给了一个原串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;
}标签:
原文地址:http://blog.csdn.net/qwb492859377/article/details/51346474