【BZOJ2054疯狂的馒头】各种瞎搞

就是瞎搞 BZOJ2054(权限题)  

2054: 疯狂的馒头

Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 1408  Solved: 598 [Submit][Status][Discuss]

Description

Input

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

Output

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

Sample Input

4 3 2 4

Sample Output

2 2 3 0

HINT

很容易想到倒着搞,面对已经涂过就不搞,涂完了就结束了。想到区间覆盖,segment tree就砸上来了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1000005;
const int maxm = 10000005;
const int inf = 1e9+7;
int n,m,p,q;
int tl[maxm],tr[maxm];
struct node {
    node *ls,*rs;
    int laz;
}z[maxn*2],*rt;int tot;
int all;
void add(node *&p,int l,int r,int x,int y,int col) {
    if(p->laz>0) return;
    if(p->laz==0&&x<=l&&r<=y) {
        p->laz = col; all+=r-l+1; return;
    }
    int mid = (l+r)>>1;
    if(y<=mid) add(p->ls,l,mid,x,y,col);
    else if(x>mid) add(p->rs,mid+1,r,x,y,col);
    else add(p->ls,l,mid,x,y,col),add(p->rs,mid+1,r,x,y,col);
    if(p->ls->laz>0&&p->rs->laz>0) {
        if(p->ls->laz==p->rs->laz) p->laz = p->ls->laz;
        else p->laz = inf; 
    }
    else p->laz = -1;
}
void bl(node *&p,int l,int r) {
    if(p->laz>0&&p->laz!=inf) {
        for(int i=l;i<=r;i++) printf("%d\n",p->laz);return;
    }
    if(l==r) {
        printf("0\n");
        return;
    }
    int mid = (l+r)>>1;
    bl(p->ls,l,mid); bl(p->rs,mid+1,r);
}
void make(node *&p,int l,int r){
    p = &z[++tot];
    if(l==r) return;
    int mid = (l+r)>>1;
    make(p->ls,l,mid);
    make(p->rs,mid+1,r);
}
int main() {
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=m;i++) {
        tl[i] = (1ll*i*p+q)%n+1; tr[i] = (1ll*i*q+p)%n+1;
        if(tl[i]>tr[i]) swap(tl[i],tr[i]);
    }
    make(rt,1,n);
    for(int i=m;i;i--) {
        add(rt,1,n,tl[i],tr[i],i);
        if(all==n) break;
    }
    bl(rt,1,n);
}
  咳咳,可我们注意到对于线段树一个结点如果涂满了再也不会变了。那么其实对于在一个单点上也是同理,只要这个单点用过了,就再也不会用了。如果我们从左向右涂色,那么意味着到某个涂过色的结点,就可以跳过那一大段,直接跳向下一个没有涂过色的结点(其实原来一次luogu月赛也见过这样的技巧,只是当时太弱,,现在更弱)。那么我们利用并查集来维护一个类似链表的东西,每次直接跳向下一个没涂的结点,然后每次暴力瞎搞就可以了(反阿克曼大法好!)。代码短多啦!(但不知为什么比线段树还慢一定我太弱了) python:
fa = [int(0)]*1000005
col = [int(0)]*1000005
def gf(x):
    #print(x,fa[x])
    if(int(fa[x])==int(x)):
        return x
    else:
        fa[x]=gf(fa[x])
        return fa[x]
    
def main():
    n,m,p,q=map(int,input().split())
    for i in range(1,n+2,1):
        fa[i]=i;
    for i in range(m,0,-1):
        l = (i*p+q)%n+1
        r = (i*q+p)%n+1
        if(l>r):swap(l,r)
        j = gf(l)
        al = 0
        while(j<=r):
            col[j]=i;
            al+=1
            fa[j]=j+1
            j=gf(j)
        if(al==n):break;
    for i in range(i,n+1):
        print(col[i])

main()


  C++:
#include<iostream>
#include<algorithm>
#include<cstdio>
 
using namespace std;
const int maxn = 1000005;
int n,m,p,q;
int fa[maxn],col[maxn],all;
int gf(int x) { return fa[x]==x?fa[x]:fa[x]=gf(fa[x]); }
int main() {
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=n+1;i++) fa[i] = i;
    for(int i=m;i;i--) {
        int l = (1ll*i*p+q)%n+1; int r = (1ll*i*q+p)%n+1;
        if(l>r) swap(l,r);
        for(int j=gf(l);j<=r;j=gf(j)) {
            col[j] = i; ++all; fa[j] = j+1;
        }
        if(all==n) break;
    }
    for(int i=1;i<=n;i++) printf("%d\n",col[i]);
}
 
posted @ 2018-10-05 00:58  Newuser233  阅读(7)  评论(0)    收藏  举报