zoukankan      html  css  js  c++  java
  • 洛谷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;
    }
    
    
  • 相关阅读:
    canvas和svg
    表单控件及表单属性
    ajax
    数据结构与算法经典问题解析-Java语言描述
    SpringBoot
    ThreadLocal 原理
    代理模式-结构型
    框架面试
    Hash算法总结
    集合与多线程面试
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10161461.html
Copyright © 2011-2022 走看看