1. 程式人生 > >Luogu P1558 色板遊戲【線段樹/狀態壓縮】By cellur925

Luogu P1558 色板遊戲【線段樹/狀態壓縮】By cellur925

題目傳送門

今天非常想再看一遍霸王別姬想不進去題於是開始刷資料結構

注意到至多隻有\(30\)種顏色,啊啊啊啊我一開始竟然想的不是狀態壓縮而是線上段樹中存一個30大小的陣列,這樣每次更新的時候暴力迴圈一遍。hhhhh。

可能這樣比較好想吧,但是比正解狀態壓縮一下不知道差到哪裡去了:)。開始還智障地把每次迴圈的次數開成30,那給出的色板數有什麼用hh,這樣是80分,改後AC。

#include<cstdio>
#include<algorithm>
#define maxn 100090

using namespace std;

int n,Q,tot;
char op[10];
struct SegmentTree{
    int l,r;int lazy;
    int col[35];
}t[maxn*4];

void re(int &x)
{
    x=0;
    char ch=getchar();
    bool flag=false;
    while(ch<'0'||ch>'9') flag|=(ch=='-'),ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    x=flag ? -x : x;
}

void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r;
    if(l==r)
    {
        t[p].col[1]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p].col[1]=t[p<<1].col[1]+t[p<<1|1].col[1];
}

void update(int p)
{
    if(!t[p].lazy||t[p].l==t[p].r) return ;
    t[p<<1].lazy=t[p].lazy;t[p<<1|1].lazy=t[p].lazy;
    for(int i=1;i<=tot;i++)
        t[p<<1].col[i]=0,t[p<<1|1].col[i]=0;
    t[p<<1].col[t[p].lazy]+=t[p<<1].r-t[p<<1].l+1;
    t[p<<1|1].col[t[p].lazy]+=t[p<<1|1].r-t[p<<1|1].l+1;
    t[p].lazy=0;
}

void change(int p,int l,int r,int id)
{
    update(p);
    if(t[p].l==l&&t[p].r==r)
    {
        for(int i=1;i<=tot;i++)
            t[p].col[i]=0;
        t[p].col[id]+=r-l+1;
        t[p].lazy=id;
        return ;
    }
    int mid=(t[p].l+t[p].r)>>1;
    if(l>mid) change(p<<1|1,l,r,id);
    else if(r<=mid) change(p<<1,l,r,id);
    else change(p<<1,l,mid,id),change(p<<1|1,mid+1,r,id);
    for(int i=1;i<=tot;i++)
        t[p].col[i]=t[p<<1].col[i]+t[p<<1|1].col[i];
}

int ask(int p,int l,int r,int id)
{
    update(p);
    if(t[p].l==l&&t[p].r==r) return t[p].col[id];
    int mid=(t[p].l+t[p].r)>>1;
    if(l>mid) return ask(p<<1|1,l,r,id);
    else if(r<=mid) return ask(p<<1,l,r,id);
    else return ask(p<<1,l,mid,id)+ask(p<<1|1,mid+1,r,id);
}

int main()
{
    scanf("%d%d%d",&n,&tot,&Q);
    build(1,1,n);   
    while(Q--)
    {
        scanf("%s",op+1);
        if(op[1]=='C')
        {
            int x=0,y=0,z=0;
            scanf("%d%d%d",&x,&y,&z);
            if(x>y) swap(x,y);
            change(1,x,y,z);
        }
        else if(op[1]=='P')
        {
            int x=0,y=0;
            scanf("%d%d",&x,&y);
            if(x>y) swap(x,y);
            int cnt=0;
            for(int i=1;i<=tot;i++)
                if(ask(1,x,y,i)>0) cnt++;
            printf("%d\n",cnt);
        }
    }
    return 0;
}

最正確的做法是把每個節點的色板情況壓成一個狀態數,更新的時候用位運算更新==。這種方法我沒有寫,找了一個大佬寫的:傳送門