ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CSP-S模拟1 [斐波那契,数颜色,分组]

2022-09-04 07:30:27  阅读:180  来源: 互联网

标签:int pos 兔子 col 斐波 MAXN 那契 include CSP


CSP-S 模拟1

洛谷上原题,不挂题面了。

A.斐波那契

P3938 斐波那契

image

观察上图,可发现规律:一个数的父亲等于这个数减去最大的小于它的斐波那契数。特殊的,如果这个数是斐波那契数,设这个数为 \(x\),\(x = fib(i)\),那它的父亲为 \(fib(i - 2)\)。

数据最大到 \(1e12\),打表发现,\(fib(60) > 1e12\)。所以预处理出来 \(fib(1) ... fib(60)\),再二分查找。

找两个点的最近公共祖先,用类似树剖的思想,一个个的往上跳。这棵树的最大深度是 \(60\),查到根总计 \(60\),均摊为 \(O(1)\)。我们把找父亲的复杂度记作 \(O(findfatℎer)\)。
总复杂度:\(O(30m×O(findfatℎer))\)。
最优复杂度:\(O(60m)\)。

Code

#include<cstdio>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXM = 3e5 + 20, SIZE = 65;
int m;
LL fib[SIZE];

inline LL read(){
    LL x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

void init(){
    fib[1] = 1, fib[2] = 1;
    for(register int i = 3; i <= 60; i++)
        fib[i] = fib[i - 1] + fib[i - 2];
}

int Find_Pos(LL num){
    int l = 1, r = 60, ans = 0;

    while(l <= r){
        int mid = (l + r) >> 1;

        if(fib[mid] <= num){
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }

    return ans;
}

LL Get_Lca(LL a, LL b){
    int pos_a, pos_b;

    while(a != b){
        pos_a = Find_Pos(a), pos_b = Find_Pos(b);

        if(pos_a < pos_b) swap(a, b), swap(pos_a, pos_b);
        a -= fib[pos_a];

        if(!a) a = fib[pos_a - 2];
    }

    return a;
}

int main(){
    init();

    m = read();
    for(register int i = 1; i <= m; i++){
        LL a, b;
        a = read(), b = read();
        
        printf("%lld\n", Get_Lca(a, b));
    }

    return 0;
}

B.数颜色

P3939 数颜色

也许上来就会想到数据结构,例如树套树,带修莫队等。不过如果真的写了任何一种上面的算法,应该是高级数据结构学傻了。

将兔子按照 (颜色, 位置) 进行双关键字排序。
操作 1 只需要在数组上二分查找;
操作 2 不会改变同种颜色兔子的相对位置,因此只需找到被交换的兔子改掉坐标即可。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

const int MAXN = 3e5 + 10;
int n, m;
int col[MAXN];
vector<int> pos[MAXN];

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read(), m = read();
    for(register int i = 1; i <= n; i++){
        col[i] = read();
        pos[col[i]].push_back(i);
    }

    for(register int i = 1; i <= m; i++){
        int opt;
        opt = read();

        if(opt == 1){
            int l, r, c;
            l = read(), r = read(), c = read();

            int pos_l = lower_bound(pos[c].begin(), pos[c].end(), l) - pos[c].begin();
            int pos_r = upper_bound(pos[c].begin(), pos[c].end(), r) - pos[c].begin();

            printf("%d\n", pos_r - pos_l);
        }
        else{
            int x, y;
            x = read(), y = x + 1;
            if(y > n) y = 1;

            if(col[x] == col[y]) continue;

            int pos_x = lower_bound(pos[col[x]].begin(), pos[col[x]].end(), x) - pos[col[x]].begin();
            int pos_y = lower_bound(pos[col[y]].begin(), pos[col[y]].end(), y) - pos[col[y]].begin();
            swap(pos[col[x]][pos_x], pos[col[y]][pos_y]);
            swap(col[x], col[y]);
        }
    }

    return 0;
}

当然,还可以用平衡树,权值线段树,主席树搞过去。再提供一种暴力分块做法,块数在 \(230\) 左右基本上就没啥问题了。

Code



#include<cmath>
#include<cstdio>
#include<algorithm>

#define register

using namespace std;

const int MAXN = 3e5 + 10, SIZE = 410;
int n, m, siz, tot;
int col[MAXN];
int belong[MAXN], st[SIZE], ed[SIZE];
unsigned short temp[SIZE][MAXN];

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

inline void write(int num){
    if(!num){
        putchar('0');
        return;
    }
    if(num < 0) putchar('-'), num = -num;

    int stk[15], top = 0;
    while(num)
        stk[++top] = num % 10, num /= 10;
    while(top)
        putchar(stk[top--] + 48);
}

void init(){
    if(n > 160000) tot = 225, siz = n / tot;
    else tot = sqrt(n), siz = n / tot;

    for(register int i = 1; i <= tot; i++){
        st[i] = siz * (i - 1) + 1;
        ed[i] = siz * i;
    }
    ed[tot] = n;

    for(register int i = 1; i <= tot; i++){
        for(register int j = st[i]; j <= ed[i]; j++){
            belong[j] = i;
            temp[i][col[j]]++;
        }
    }
}

int Query_Col(int l, int r, int val){
    int ans = 0;

    if(belong[l] == belong[r]){
        for(register int i = l; i <= r; i++)
            if(col[i] == val) ans++;
    }
    else{
        for(register int i = l; i <= ed[belong[l]]; i++)
            if(col[i] == val) ans++;
        for(register int i = st[belong[r]]; i <= r; i++)
            if(col[i] == val) ans++;
        for(register int i = belong[l] + 1; i < belong[r]; i++)
            ans += temp[i][val];
    }

    return ans;
}

void Update(int pos){
    int x = pos, y = pos + 1;
    if(y > n) y = 1;

    if(belong[x] != belong[y]){
        temp[belong[x]][col[x]]--;
        temp[belong[x]][col[y]]++;
        temp[belong[y]][col[y]]--;
        temp[belong[y]][col[x]]++;
    }

    swap(col[x], col[y]);
}

int main(){
    n = read(), m = read();
    for(register int i = 1; i <= n; i++)
        col[i] = read();
    
    init();

    for(register int i = 1; i <= m; i++){
        int opt;
        opt = read();

        if(opt == 1){
            int l, r, c;
            l = read(), r = read(), c = read();
            write(Query_Col(l, r, c)), putchar('\n');
        }
        else{
            int x;
            x = read();
            Update(x);
        }
    }

    return 0;
}

C.分组

P3940 分组

\(k = 1\) 时,外循环枚举每一只兔子的颜色值 \(a_i\),内循环设 \(x\) 从 \(1\) 枚举到 \(512\)(\(=\sqrt{131072×2}\)),看看有没有访问过 \((x^2-col_i)\) 这个值。如果访问过,则第i只兔子与当前组内其它兔子有冲突,需要清空访问标记并且单独新建一个分组;如果没访问过,则令第i只兔子加入当前分组。最后再打上访问标记即可。

\(k = 2\) 时,思路和 \(k = 1\) 相似,但要用到关系并查集。如果访问过 \((x^2-col_i)\),我们仍然可以试图将 \(a_i\) 加入当前分组,因为此时我们的分组里允许有两个小团体。我们此时可以使用并查集来维护敌对关系:fa[] 数组开2倍空间, find(1~n) 代表每只兔子所在小团体, find(n+1~2n) 代表每只的兔子所在小团体的敌对小团体

在上述情况下当访问过 \((x^2-col_i)\) 时,我们先判断下颜色值为 \((x^2-col_i)\) 的兔子(开动态数组 vector 存下每一只当前分组内相同颜色的兔子编号。)和第 i 只兔子是否已经被规划进入同一小团体中。如果已经被规划入同一小团体,那么便不得不清空访问并新建分组。否则,将第 \(i\) 只兔子与颜色值为 \((x^2-col_i)\) 的兔子分别互相加入对方的敌对集合即可。

Code

#include<stack>
#include<cstdio>
#include<vector>

using namespace std;

const int MAXN = 140010;
int k, n, tot, last;
int col[MAXN];
stack<int> ans;
vector<int> vis[MAXN << 1];

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

struct Union_Set{
    int fa[MAXN << 1];

    void init(int m){
        for(register int i = 1; i <= m; i++)
            fa[i] = i;
    }

    int Find(int x){
        return fa[x] == x ? x : fa[x] = Find(fa[x]);
    }
}U;

void Clear(int &pre, int l){
    for(register int i = pre - 1; i > l; i--)
        vis[col[i]].clear();
    pre = l + 1;
    ans.push(pre - 1);
}

void Solve1(){
    for(register int i = n; i >= 1; i--){
        bool IsFind = true;
        for(register int j = 1; j <= 512; j++){
            if(j * j - col[i] >= 0 && vis[j * j - col[i]].size()){
                IsFind = false;
                break;
            }
        }

        if(!IsFind) Clear(last, i);
        vis[col[i]].push_back(1);
    }
}

void Solve2(){
    U.init(n << 1);
    
    for(register int i = n; i >= 1; i--){
        for(register int j = 1; j <= 512; j++)
            if(j * j - col[i] >= 0 && vis[j * j - col[i]].size())
                for(register int k = 0; k < vis[j * j - col[i]].size(); k++){
                    int t = vis[j * j - col[i]][k];
                    if(U.Find(i) == U.Find(t)) Clear(last, i);
                    else{
                        U.fa[U.Find(i + n)] = U.Find(t);
                        U.fa[U.Find(t + n)] = U.Find(i);
                    }
                }
        vis[col[i]].push_back(i);
    }
}

void Print(){
    printf("%ld\n", ans.size() + 1);
    while(!ans.empty())
        printf("%d ", ans.top()), ans.pop();
}

int main(){
    n = read(), k = read(), last = n + 1;
    for(register int i = 1; i <= n; i++)
        col[i] = read();
    
    if(k == 1) Solve1();
    else Solve2();

    Print();

    return 0;
}

标签:int,pos,兔子,col,斐波,MAXN,那契,include,CSP
来源: https://www.cnblogs.com/TSTYFST/p/16654193.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有