NOIP模拟赛 斐波那契树 + 二分 + 并查集

来源:互联网 发布:淘宝一次破损补寄条款 编辑:程序博客网 时间:2024/06/04 19:59

FIbonacci

这里写图片描述

m <= 3e5. 询问的数字是10^12级别的.

题解

首先很明显肯定要log的算法. 并且由于斐波那契的性质, 树高一定是log级别的. 那么对于两个点我们只要暴力跳fa即可过.
问题就在于怎么快速求father.
比如编号i的他的fa是谁. 我们想这个树每次生成f[j]个(f是斐波那契数列). 那么i一定是某一批斐波那契数列. 那么我们知道这一批之前一共生成了多少个的话, 那么用i-这个数量之后就是i在这一批第几个生成, 那么也就是i的父亲.
然而之前一共生成的实际上也是一个斐波那契数, 并且是刚好比i小的斐波那契数.
因为你看这棵树初始三批都是1. 那么i之前的数量就是f[1] + f[1] + f[2] + f[3] + f[4] + …f[i - 1] (f[1] = f[2] = 1). 我们把f[1]跟f[1]配生成f[2], f[2]跟f[3]生成f[4]…模拟一下就发现多了一个f[1]之后就也是一个斐波那契数. 也就是说斐波那契数的前缀和 + 1也是一个斐波那契数.
那么每次找fa二分即可. fa就是自己 - 刚好比自己小的斐波那契数. 二分斐波那契数列即可. 注意斐波那契数只需60项就 > 10 ^ 12.

#include<stdio.h>#include<map>using namespace std;typedef long long dnt;int T;dnt f[65];inline int find(dnt pos){    int lf = 1, rg = 60, ans = 1;    while(lf <= rg){        int mid = (lf + rg) >> 1;        if(f[mid] < pos) ans = mid, lf = mid + 1;        else rg = mid - 1;    }    return ans;}inline void init(){    f[1] = 1;       for(int i = 2; i <= 60; ++i)        f[i] = f[i - 1] + f[i - 2];}int main(){    freopen("fibonacci.in", "r", stdin);    freopen("fibonacci.out", "w", stdout);    init();    scanf("%d", &T);    while(T--){        dnt a, b;        scanf("%I64d%I64d", &a, &b);        while(true){            if(a == b) {printf("%I64d\n", a); break;}            if(a < b) swap(a, b);            a -= f[find(a)];        }    }    return 0;}

Color

给定一个正整数序列, 询问l到r值c的有多少个. 支持将i+1和i+2的值交换的修改操作. 询问及操作数量 <= 3e5, n <= 3e5, ai <= 3e5.

题解

班上居然很多人写了主席树…一看这道题就是普通的二分.
用vector存每个值在整个序列中出现的位置(单调上升). 那么询问l到r的c有多少个就在c的vector里二分即可.
交换操作也是直接修改.

Division

这里写图片描述

K <= 2, ai <= 130000, n <= 130000.

题解

贪心的想, 肯定是从后往前选, 每一段选得越多越好. 这样就既能满足字典序最小并且组数最少.
那么现在就考虑每一段如何check合不合法.
如果K == 1的话, 直接枚举平方数即可. 就520个.
K = 2的话. 判断一段发现就是分成两个集合. 会发现跟bzoj团伙或者noip关押罪犯的镜像并查集一样.
注意某ai*2也是平方数的话就要特判.

阅读全文
0 0
原创粉丝点击