1. 程式人生 > >洛谷3613睡覺困難綜合徵(LCT維護鏈資訊(前後綴)+貪心)

洛谷3613睡覺困難綜合徵(LCT維護鏈資訊(前後綴)+貪心)

這個題目還是很好啊QWQ很有紀念意義

首先,如果在序列上且是單次詢問的話,就是一個非常裸的貪心了QWQ這也是NOI當時原題的問題和資料範圍

我們考慮上樹的話,應該怎麼做?

我的想法是,對於每一位建一個LCT來做,然後對一個點x維護,當前是0到鏈頂的結果,當前是1到頂的結果,當前是0到底的結果,當前是1到底的結果(之所以維護後兩個是因為\(reverse\)操作的存在)

這樣的話,對於每次詢問,我就可以直接\(split\),然後貪心的詢問。

不過很可惜,這個演算法的複雜度是\(O(qklogn)\)
並不能跑過(雖然沒試過硬卡常行不行)

那麼應該怎麼做呢QWQ 這時候,我選擇了看題解。

首先,經過仔細思考,我們會發現,QWQ對於題目來說,其實並不需要按位來做,可以直接維護\(f_0,f_1,g_0,g_1\)四個值,分別表示所有位0到鏈頂的結果,所有二進位制位全是1的數到頂的結果,所有位全是0到底的結果,所有位全是1到底的結果

那麼這個東西應該怎麼轉移呢?

這裡合併的大概思想是 全0走到中間節點之後,是0的那幾位,相當於後面的全0走的,是1的那幾位,相當於是後面全1走的 ,全1的也是同理

而之所以這麼寫,是因為運用了與的美妙的性質,只有兩邊都是1,最終結果才是1。

所以,假設對於當前是全零0來說,考慮前一半,是計算0的那幾位,要是採用了&與這個運算,如果最後走完,\(f_0\)

的某一位是0,那麼最一開始無論是什麼,最終&完都是0,也就不會存在一開始是1的那些位影響答案。

要是這一位是1,我們要滿足與完 是1的話,同時去除一開始是1的位的貢獻,我們就需要先取反,然後再&

其他的其實也是同理

Node merge(Node a,Node b) //我們預設a在前,b在後 
{
    Node c;
    c.f0=(~a.f0 & b.f0) + (a.f0 & b.f1); //這裡合併的大概思想是 全0走到中間節點之後,是0的那幾位,相當於後面的全0走的,是1的那幾位,相當於是後面全1走的 
    c.f1=(~a.f1 & b.f0) + (a.f1 & b.f1); //而之所以這麼寫,是因為運用了與的美妙的性質,只有兩邊都是1,最終結果才是1。所以,假設對於當前是0來說,只有後面全零弄出來是1,我們當前的的答案才是1,那麼為了出來1,我們就需要先取反,然後再& 
    return c;
}

void update(unsigned long long x)
{
    zheng[x]=fan[x]=val[x];
    if (ch[x][0])
    {
        zheng[x]=merge(zheng[x],zheng[ch[x][0]]);
        fan[x]=merge(fan[ch[x][0]],fan[x]); 
    }
    if (ch[x][1])
    {
        zheng[x]=merge(zheng[ch[x][1]],zheng[x]);
        fan[x]=merge(fan[x],fan[ch[x][1]]); 
    }
}

其他的LCT操作的話,也就和別的沒什麼區別了。

這裡再講一下底下那個貪心的過程

unsigned long long ans=ling;
        split(x,y);
        //cout<<1<<endl;
        for (long long i=k-1;i>=0;i--)
        {
            if (fan[y].f0&power[i]) 
            {
              ans=ans+power[i];
              continue;
            }
            if (fan[y].f1 & power[i])
            {
                if (power[i]>z) continue;
                ans=ans+power[i];
                z-=power[i];
            }
            //cout<<i<<endl;
        }
        cout<<ans<<"\n";

從高位向低位考慮,如果當前位填0,最終結果是1的話,那麼就填0。
如果當前位填1,最終結果才能是1。我們就需要比較一下剩餘的值是否比這個位填1的數大,大的話才能填

以此類推

QWQ

記得開\(unsigned\ long\ long\)

上程式碼

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
#define lc ch[x][0]
#define rc ch[x][1]

using namespace std;

inline unsigned long long read()
{
  unsigned long long x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 1e6+1e2;

struct Node{
    unsigned long long f0,f1;
};
unsigned long long ch[maxn][3];
unsigned long long rev[maxn],fa[maxn];
Node zheng[maxn],fan[maxn];
unsigned long long n,m;
Node val[maxn];
unsigned long long ling =0;

unsigned long long son(unsigned long long x)
{
    if (ch[fa[x]][0]==x) return 0;
    else return 1; 
}

bool notroot(unsigned long long x)
{
    return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}

void reverse(unsigned long long x)
{
    swap(ch[x][0],ch[x][1]);
    swap(zheng[x].f0,fan[x].f0);
    swap(zheng[x].f1,fan[x].f1);
    rev[x]^=1;
}

Node merge(Node a,Node b) //我們預設a在前,b在後 
{
    Node c;
    c.f0=(~a.f0 & b.f0) + (a.f0 & b.f1); //這裡合併的大概思想是 全0走到中間節點之後,是0的那幾位,相當於後面的全0走的,是1的那幾位,相當於是後面全1走的 
    c.f1=(~a.f1 & b.f0) + (a.f1 & b.f1); //而之所以這麼寫,是因為運用了與的美妙的性質,只有兩邊都是1,最終結果才是1。所以,假設對於當前是0來說,只有後面全零弄出來是1,我們當前的的答案才是1,那麼為了出來1,我們就需要先取反,然後再& 
    return c;
}

void update(unsigned long long x)
{
    zheng[x]=fan[x]=val[x];
    if (ch[x][0])
    {
        zheng[x]=merge(zheng[x],zheng[ch[x][0]]);
        fan[x]=merge(fan[ch[x][0]],fan[x]); 
    }
    if (ch[x][1])
    {
        zheng[x]=merge(zheng[ch[x][1]],zheng[x]);
        fan[x]=merge(fan[x],fan[ch[x][1]]); 
    }
}

void pushdown(unsigned long long x)
{
    if (rev[x])
    {
        if (ch[x][1]) reverse(ch[x][1]);
        if (ch[x][0]) reverse(ch[x][0]);
        rev[x]=0;
    }
}

void rotate(unsigned long long x)
{
    unsigned long long y=fa[x],z=fa[y];
    unsigned long long b=son(x),c=son(y);
    if (notroot(y)) ch[z][c]=x;
    fa[x]=z;
    ch[y][b]=ch[x][!b];
    fa[ch[x][!b]]=y;
    ch[x][!b]=y;
    fa[y]=x;
    update(y);
    update(x);
}

unsigned long long st[maxn];

void splay(unsigned long long x)
{
    unsigned long long y=x,cnt=0;
    st[++cnt]=y;
    while (notroot(y)) y=fa[y],st[++cnt]=y;
    while (cnt) pushdown(st[cnt--]);
    while (notroot(x))
    {
        unsigned long long y=fa[x],z=fa[y];
        unsigned long long b=son(x),c=son(y);
        if (notroot(y))
        {
            if (b==c) rotate(y);
            else rotate(x); 
        }
        rotate(x);
     } 
     update(x);
}

void access(unsigned long long x)
{
    for (unsigned long long y=0;x;y=x,x=fa[x])
    {
        splay(x);
        ch[x][1]=y;
        update(x);
    }
}

void makeroot(unsigned long long x)
{
    access(x);
    splay(x);
    reverse(x);
}

unsigned long long findroot(unsigned long long x)
{
    access(x);
    splay(x);
    while (ch[x][0])
    {
        pushdown(x);
        x=ch[x][0];
    } 
    return x; 
}

void split(unsigned long long x,unsigned long long y)
{
    makeroot(x);
    access(y);
    splay(y);
}

void link(unsigned long long x,unsigned long long y)
{
    makeroot(x);
    if(findroot(y)!=x) fa[x]=y;
}

unsigned long long power[maxn];

unsigned long long ymh = 2;
unsigned long long k;

signed main()
{
  n=read(),m=read(),k=read();
  power[0]=1;
  for (unsigned long long i=1;i<64;i++) power[i]=power[i-1]*ymh;
  
  for (unsigned long long i=1;i<=n;i++)
  {
    unsigned long long y=read(),x=read();
    if (y==1)
    {
        val[i]=(Node){ling,x};
    }
    if (y==2)
    {
        val[i]=(Node){x,~ling};
    }
    if (y==3)
    {
        val[i]=(Node){x,~x};
    } 
  }
  for (unsigned long long i=1;i<n;i++)
  {
    unsigned long long x=read(),y=read();
    link(x,y);
  } 
  
  //cout<<1<<endl;
  for (unsigned long long i=1;i<=m;i++)
  {
     unsigned long long opt=read(),x=read(),y=read(),z=read();
     if (opt==2)
     {
       splay(x);
       if (y==1) val[x]=(Node){ling,z};
       if (y==2) val[x]=(Node){z,~ling};
       if (y==3) val[x]=(Node){z,~z}; 
     }
     if (opt==1)
     {
        unsigned long long ans=ling;
        split(x,y);
        //cout<<1<<endl;
        for (long long i=k-1;i>=0;i--)
        {
            if (fan[y].f0&power[i]) 
            {
              ans=ans+power[i];
              continue;
            }
            if (fan[y].f1 & power[i])
            {
                if (power[i]>z) continue;
                ans=ans+power[i];
                z-=power[i];
            }
            //cout<<i<<endl;
        }
        cout<<ans<<"\n";
     }
  }
  return 0;
}