P1558 色板遊戲(線段樹+位運算)

題目鏈接:https://www.luogu.org/problem/P1558
題目背景
阿寶上學了,今天老師拿來了一塊很長的塗色板。

題目描述
色板長度爲L,L是一個正整數,所以我們可以均勻地將它劃分成L塊1釐米長的小方格。並從左到右標記爲1, 2, … L。

現在色板上只有一個顏色,老師告訴阿寶在色板上只能做兩件事:

“C A B C” 指在A到 B 號方格中塗上顏色 C。
“P A B” 指老師的提問:A到 B號方格中有幾種顏色。
學校的顏料盒中一共有 T 種顏料。爲簡便起見,我們把他們標記爲 1, 2, … T. 開始時色板上原有的顏色就爲1號色。 面對如此複雜的問題,阿寶向你求助,你能幫助他嗎?

輸入格式
第一行有3個整數 L (1 <= L <= 100000), T (1 <= T <= 30) 和 O (1 <= O <= 100000)。 在這裏O表示事件數。
接下來 O 行, 每行以 “C A B C” 或 “P A B” 得形式表示所要做的事情(這裏 A, B, C 爲整數, 可能A> B,這樣的話需要你交換A和B)

輸出格式
對於老師的提問,做出相應的回答。每行一個整數。

輸入輸出樣例
輸入 #1
2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2
輸出 #1
2
1
題意: 兩個操作,一個區間可覆蓋塗色;一個區間詢問有多少中 顏色。
思路:

  1. 看到顏色覆蓋,以爲是區間染色問題,不過如果這個題的塗色的是每個點,而不是兩點之間的區間,這樣就比較好解決。
  2. 一種思路,改變顏色時,如果當前節點的兩個子節點不同,則說明還未到最低端,賦爲-1。如果兩個子節點相同,說明該區間內的顏色相同,則隨便一個賦值給當前節點。仔細思考一下就明白。
  3. 另一個看的其他大佬的思路,題目給的顏色只有30種,所以將顏色狀態壓縮,轉爲2的k次方,既每一種顏色代表2的幾次方。這樣處理就可以用線段樹進行單點修改,詢問的時候只需起來。
  4. 位運算小知識:
    1<<(a-1)爲構造一個只有a位置爲1,其他爲0的二進制數。
    或在二進制中就相當於“加”,保存1,就不用多說。在上面狀態壓縮的鏈接中有詳細證明。
    下面是兩種思路的代碼:
    普通解法ac代碼:
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define ll long long
using namespace std;
const int maxn=1e5+7;
const ll INF=1e9;
ll sum[maxn*4],add[maxn*4];
ll vis[maxn];
void pushdown(ll root)
{
    if(add[root])
    {
        add[root<<1]=add[root];
        add[root<<1|1]=add[root];
        sum[root<<1]=add[root];
        sum[root<<1|1]=add[root];
        add[root]=0;
    }
}
void change(ll root,ll l,ll r,ll ql,ll qr,ll k)
{
    if(ql<=l&&qr>=r)
    {
        sum[root]=add[root]=k;
        return ;
    }
    ll mid=(l+r)>>1;
    pushdown(root);
    if(ql<=mid)     change(root<<1,l,mid,ql,qr,k);
    if(qr>mid)      change(root<<1|1,mid+1,r,ql,qr,k);
    if(sum[root<<1]==sum[root<<1|1])
        sum[root]=sum[root<<1];
    else
        sum[root]=-1;
}
void q(ll root,ll l,ll r,ll ql,ll qr)
{
    if(ql<=l&&qr>=r)
    {
        if(sum[root]!=-1)
        {
            vis[sum[root]]=1;
            return ;
        }
    }
    pushdown(root);
    ll mid=(l+r)>>1;
    if(ql<=mid)     q(root<<1,l,mid,ql,qr);
    if(qr>mid)      q(root<<1|1,mid+1,r,ql,qr);
}
int main()
{
    ll n,t,o;
    scanf("%lld%lld%lld",&n,&t,&o);
    for(int i=1;i<=n;i++)
        change(1,1,n,i,i,1);
    char a[2];
    for(int i=1;i<=o;i++)
    {
        scanf("%s",a);
        if(a[0]=='C')
        {
            ll A,B,C;
            scanf("%lld%lld%lld",&A,&B,&C);
            if(A>B)
                swap(A,B);
            change(1,1,n,A,B,C);
        }
        else
        {
            ll A,B;
            scanf("%lld%lld",&A,&B);
            if(A>B)
                swap(A,B);
            ll num=0;
            memset(vis,0,sizeof(vis));
            q(1,1,n,A,B);
            for(int i=1;i<=t;i++)
            {
                if(vis[i])
                    num++;
            }
            printf("%lld\n",num);
        }
    }
    return 0;
}

位運算解法ac代碼:

#include<stdio.h>
#include<algorithm>
#include<string.h>
#define ll long long
using namespace std;
const int maxn=2e5+7;
const ll INF=1e9;
ll sum[maxn*4],add[maxn*4];
void build(ll root,ll l,ll r)
{
    add[root]=0;
    if(l==r)
    {
        sum[root]=1;
        return;
    }
    ll mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    sum[root]=sum[root<<1]|sum[root<<1|1];
}
void pushdown(ll root)
{
    if(add[root])
    {
        add[root<<1]=add[root];
        add[root<<1|1]=add[root];
        sum[root<<1]=add[root];
        sum[root<<1|1]=add[root];
        add[root]=0;
    }
}
void change(ll root,ll l,ll r,ll ql,ll qr,ll k)
{
    if(ql<=l&&qr>=r)
    {
        sum[root]=add[root]=k;
        return ;
    }
    ll mid=(l+r)>>1;
    pushdown(root);
    if(ql<=mid)   change(root<<1,l,mid,ql,qr,k);
    if(qr>mid)    change(root<<1|1,mid+1,r,ql,qr,k);
    sum[root]=sum[root<<1]|sum[root<<1|1];
}
ll q(ll root,ll l,ll r,ll ql,ll qr)
{
    if(ql<=l&&qr>=r)
        return sum[root];
    ll mid=(l+r)>>1;
    pushdown(root);
    ll ans=0;
    if(ql<=mid)       ans|=q(root<<1,l,mid,ql,qr);
    if(qr>mid)        ans|=q(root<<1|1,mid+1,r,ql,qr);
    return ans;
}
int main()
{
    ll n,t,m;
    scanf("%lld%lld%lld",&n,&t,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        char a[2];
        scanf("%s",a);
        if(a[0]=='C')
        {
            ll A,B,C;
            scanf("%lld%lld%lld",&A,&B,&C);
            if(A>B)
                swap(A,B);
            ll cnt=1<<(C-1);
            change(1,1,n,A,B,cnt);
        }
        else
        {
            ll A,B;
            scanf("%lld%lld",&A,&B);
            if(A>B)
                swap(A,B);
            ll ans=0,num=0;
            ans=q(1,1,n,A,B);
            //printf("ans: %lld\n",ans);
            for(int i=1;i<=t;i++)
            {
                if(ans&(1<<(i-1)))
                    num++;
            }
            printf("%lld\n",num);
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章