BZOJ2375

2375: 疯狂的涂色

Time Limit: 5 Sec Memory Limit: 128 MB
Submit: 436 Solved: 160

Description

小t非常喜爱画画,但是他还是一个初学者。他最近费尽千辛万苦才拜到已仙逝的达芬奇为师(神仙?妖怪?谢谢)。达芬奇果然是画鸡蛋长大的,让小t一入门就拿着一张白纸条疯狂地涂色。假设纸条被划分成了n个区域,用1~n的整数从左到右顺序编号,达芬奇总共下达了m条指令。第I条指令是让小t把编号为(Ip+q)mod n+1与(Iq+p)mod n+1(p,q为常整数)之间的区域(连续的一段区域)涂成第I种颜色。你可以假设达芬奇家中颜料的颜色数足够多(达芬奇是画鸡蛋长大的)。

现在由于达芬奇下达的指令过多,小t一时应付不过来。达芬奇只让他回答每一个区域最后的颜色。趁达芬奇还在“五谷轮回之所”忙碌时,小t偷偷的请让你这个计算机高手帮他算出最后的颜色状态,并告诉他。时间紧迫,要快哟!(达芬奇的指令次数多到恶心)

Input

为四个整数n,m,p,q。

Output

n行,第I行代表最后第I个格子的颜色。白色编号为0。

Sample Input

1000 999 341 547

Sample Output

自行脑补

解题思路:

这道题刚开始时我天真的想用线段树,可后来发现是自己想多了。直到看了题解,才知道完全就是并查集的经典应用。我们倒着来涂色,就可以保证在没有涂色的地方涂上的颜色一定是最后的颜色,并且假如我们涂了第i块,就将第i块扔到第i+1块的集合里面去,这样就可以使得每次我们都可以跳着涂了。

代码如下:

#include <iostream>
#include <cstdio>
using namespace std;

int read(){
    int ans = 0,flag = 1;
    char c = getchar();
    for(;!isdigit(c); c = getchar())if(c == '-')flag = -1;
    for(; isdigit(c); c = getchar())ans = (ans<<1) + (ans<<3) + c - 48;
    return flag*ans;
}

const int maxn = 10000000 + 5;

int f[maxn],color[maxn];
int n,m,p,q;

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

int main(){
    n = read();m = read();p = read();q = read();
    for(int i = 1;i <= n+1; i++)f[i] = i;
    for(int i = m;i >= 1; i--){
        int left = (i*p+q)%n+1;
        int right = (i*q+p)%n+1;
        if(left > right)swap(left,right);
        for(int j = Find(left);j <= right; j = Find(j+1)){//因为集合里相当于存的是当前这块所在的连续的已经涂了颜色的区间的最右边+1的位置,所以直接跳就可以了。
            color[j] = i;
            f[j] = Find(j+1);//合并
        }
        if(Find(1) == n+1)break;//如果整个区间都已近被涂了就跳出
    }
    for(int i = 1;i <= n; i++)printf("%d\n",color[i]);
    return 0;
}

posted @ 2017-08-29 17:47  Frade~  阅读(195)  评论(0编辑  收藏  举报