1. 程式人生 > >bzoj 2054: 瘋狂的饅頭(線段樹||並查集)

bzoj 2054: 瘋狂的饅頭(線段樹||並查集)

sin printf define mes date tps c++ lin 染色

鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=2054

線段樹寫法:

點的顏色只取決於最後一次染的顏色,所以我們可以倒著維護,如果當前區間之前被染過了,就不用再染了,對區間染色我們可以暴力在線段樹上進行更新,並用線段樹維護下那些區間已經被染色了,被染色的區間更新的時候直接跳過,這樣可以節省很多時間。

實現代碼:

#include<bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define
mid int m = (l + r) >> 1 const int M = 1e6 + 10; int sum[M<<2]; void pushup(int rt){ sum[rt] = sum[rt<<1]&&sum[rt<<1|1]; } void update(int L,int R,int c,int l,int r,int rt){ if(sum[rt]) return ; if(l == r){ sum[rt] = c; return
; } mid; if(L <= m) update(L,R,c,lson); if(R > m) update(L,R,c,rson); pushup(rt); } void ct(int l,int r,int rt){ if(l == r){ printf("%d\n",sum[rt]); return ; } mid; ct(lson); ct(rson); } int main() { int n,m,p,q; scanf(
"%d%d%d%d",&n,&m,&p,&q); for(int i = m;i >= 1;i --){ int x = ((i*p+q)%n)+1; int y = ((i*q+p)%n)+1; if(x > y) swap(x,y); update(x,y,i,1,n,1); } ct(1,n,1); }

並查集寫法:

和線段樹的思路一樣,我們需要快速跳過已染色區間,這裏用並查集維護已染色的區間,區間更新時可以利用並查集快跳過被染色的區間。

實現代碼:

#include<bits/stdc++.h>
using namespace std;
const int M = 1e6+10;
int ans[M],f[M];
int Find(int x){
    if(x == f[x]) return x;
    return f[x] = Find(f[x]);
}

int main()
{
    int n,m,p,q;
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i = 0;i <= n+1;i ++) f[i] = i;
    for(int i = m;i >= 1;i --){
        int x = ((i*p+q)%n)+1,y = ((i*q+p)%n)+1;
        if(x > y) swap(x,y);
        for(int j = Find(x);j <= y;j = Find(j+1)){
            f[j] = Find(j+1); ans[j] = i;
        }
    }
    for(int i = 1;i <= n;i ++)
        printf("%d\n",ans[i]);
    return 0;
}

bzoj 2054: 瘋狂的饅頭(線段樹||並查集)