其他分享
首页 > 其他分享> > 并查集用法——快速跳过区间

并查集用法——快速跳过区间

作者:互联网

题目:

例一:疯狂的馒头

Description

小t十分喜欢吃馒头。兴奋之下他一下子买了N个馒头请所有认识他的人吃。
但是小t不喜欢白色,喜欢红色、黄色、绿色等鲜艳的颜色。于是他把所有白色的馒头排成一列。然后进行M次染色操作。每个染色操作都是用一个神奇的刷子把连续的多个馒头染成特定的某种颜色。一个馒头最终的颜色是最后一次染它的颜色。如果一个馒头没有被染过色,那么它的颜色就是白色。
现在小t已经定好了染色计划:在第i次染色操作中,把第(i×p + q)mod N + 1个馒头和第(i×q + p)mod N + 1个馒头之间的馒头染成颜色i,其中p,q是特定的两个正整数。他想立即知道最后每个馒头的颜色。你能帮他吗?

Input

第一行四个正整数N,M,p,q。

Output

一共输出N行,第i行表示第i个馒头的最终颜色(如果最终颜色是白色就输出0)。

Sample Input

4 3 2 4

Sample Output

2 2 3 0

HINT

20%数据满足:1≤n≤1000,1≤m≤10000;

40%数据满足:1≤n≤10000,1≤m≤100000;

100%数据满足:1≤n≤1000000,1≤m≤10000000;1≤mp+q,mq+p≤2^31-1;

分析: 一眼就想到了倒着做,而且发现了馒头被染成哪种颜色与染色的次序相同,但你倒着做的时候也不要是纯模拟,那样和正着做没啥区别,这个时候你会想到如何快速跳过已经染完色的馒头。在这可以选择用并查集

solve: 每次将已染色的馒头连到它之后的第一个未染色的馒头上,这样 一来如果我们走到一个已染色的馒头上,我们就会立即找到它之后的没有染色的馒头。

注意: 范围貌似支持开long long 而且看见一篇博文上说写递归会报RE...

#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define MAX 1000000+99

LL n,m,p,q;
int fa[MAX];//fa[i] 表示 i 后面第一个没有被染过色的馒头 
int ans[MAX];

int find(LL x) {
//  return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
    //非递归写法 
    int r=x,pre;
    while(r!=fa[r])  r=fa[r];
    while(x!=r){
        pre=fa[x];
        fa[x]=r;//路径压缩 
        x=pre;
    }
    return r;
}

int main() {
    scanf("%lld %lld %lld %lld",&n,&m,&p,&q);
    for(int i = 1; i<= n; i++) fa[i] = i;
    for(int i = m; i >= 1; i--) {
        LL l = (LL)(i * p + q)% n + 1;
        LL r = (LL)(i * q + p)% n + 1;
        if(l > r) swap(l,r);
        for(int j = find(l); j <= r; j = find(j)) {
            ans[j] = i;
            fa[j] = j+1;
        }
    }
    for(int i = 1; i <= n; i++) printf("%d ",ans[i]);
    return 0;
}

标签:馒头,颜色,int,染色,LL,查集,用法,fa,跳过
来源: https://www.cnblogs.com/virtualtan/p/10978164.html