倍增法+二分 hnu13547 Lily'game

传送门:点击打开链接

题意:给了一个原串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;
}

倍增法+二分 hnu13547 Lily'game

时间: 2024-07-30 10:18:34

倍增法+二分 hnu13547 Lily'game的相关文章

LightOJ - 1128 (倍增法 + dp)

唔 已经有一个多月没有写题解了, 其实这一个多月也是做了一些题目的,但是就是比较懒啊,所以都堆在一起没有写了--. 题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26842 题意: 给出一棵树(有默认根), 每个节点有一个权值,再给出q个询问,每个询问包含v k两个变量, 让你输出树根和v节点的路径上,权值大于等于k的离树根最近的节点. 思路: 算是倍增法 + dp的入门练习吧. dp[v][i] 表示节点v

倍增法

转自http://i.cnblogs.com/EditPosts.aspx?opt=1:很容易懂 1. DFS预处理出所有节点的深度和父节点  void dfs(int u){    int i;    for(i=head[u];i!=-1;i=next[i])      {          if (!deep[to[i]])        {                        deep[to[i]] = deep[u]+1;            p[to[i]][0] = u

UVa 11149 矩阵的幂(矩阵倍增法模板题)

https://vjudge.net/problem/UVA-11149 题意: 输入一个n×n矩阵A,计算A+A^2+A^3+...A^k的值. 思路: 矩阵倍增法. 处理方法如下,一直化简下去直到变成A. 代码如下: 1 Matrix solve(Matrix base,int x) 2 { 3 if(x==1)return base; 4 Matrix temp=solve(base,x/2); 5 Matrix sum=add(temp,multi(pow(base,x/2),temp)

poj1330Nearest Common Ancestors以及讲解倍增法求lca

Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20487   Accepted: 10784 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each node is labeled with an

对分查找法(二分查找法,折半查找法)

二分查找法是针对已经排好序的序列进行查找 每次折半查找 算法时间复杂度,对于长度为N的序列,每次执行N/2,假设k次结束,最后到第一个N/2^k=0,所以k=logN 时间复杂度logN int binarysearch(const int array[], int x, int N) { int low, mid, high; low = 0, high = N - 1; while (low <= high) { mid = (low + high) / 2; if(array[mid] <

Codeforces 519E A and B and Lecture Rooms [倍增法LCA]

题意: 给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的.树上所有边的边权是1. 思路: 很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分情况讨论. 一开始困扰我的问题是如果lca不是正中间的点,如何在比较低的复杂度的层面上求解中点. 倍增法lca不光可以在logn的时间复杂度内查询某两个点的lca,还可以实现在logm的时间复杂度能查询某个节点的第m个父亲节点. 算法的核心是用二进制的运算来实现查询. #include<bits/s

倍增法求LCA

倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可以在O(logn)的时间内求出求出任意节点的任意祖先. 然后先把两个节点中转化为深度相同的节点,然后一起向上递增,知道找到相同的节点,该节点就是这两个节点的最近公共祖先. 代码实现: 1 #include<cstdio> 2 #include<iostream> 3 #define N

[POJ 1330] Nearest Common Ancestors (倍增法)

题目同上篇,最近公共祖先. 因为没有清零tot,RE了好多次TAT 一定要初始化啊!! 1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 using namespace std; 6 int root,head[10010]; 7 int next[10010],to[10010],tot; 8 int deep[10010],n; 9 int p[

UVa 11149 Power of Matrix (矩阵快速幂,倍增法或构造矩阵)

题意:求A + A^2 + A^3 + ... + A^m. 析:主要是两种方式,第一种是倍增法,把A + A^2 + A^3 + ... + A^m,拆成两部分,一部分是(E + A^(m/2))(A + A^2 + A^3 + ... + A^(m/2)),然后依次计算下去,就可以分解,logn的复杂度分解,注意要分奇偶. 另一种是直接构造矩阵,,然后就可以用辞阵快速幂计算了,注意要用分块矩阵的乘法. 代码如下: 倍增法: #pragma comment(linker, "/STACK:10