洛谷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<<"
";

从高位向低位考虑,如果当前位填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<<"
";
	 }
  }
  return 0;
}

原文地址:https://www.cnblogs.com/yimmortal/p/10161461.html