zoukankan      html  css  js  c++  java
  • 神奇的结构体

    在篇头,首先需要感谢的就是给予作者指导的神 ( ext{JacderZhang}) ,感谢。

    引子

    有一天,作者在虚树板子题的时候突然犯懒了,想利用一个结构同时维护两种树的信息,其中一种树我需要有倍增,而另外一种树我不需要,于是我突发奇想可不可以搞一个结构体,然后我可以选择是否开出我倍增需要的空间,从而起到优化空间和代码复杂度的问题(实际上并没有优化代码复杂度)。

    然后我写出了这样的一段代码:

    struct Tree{
        struct Node{int dep;}tr[N];
        Tree(){memset(tr,0,sizeof(tr));}
        struct Edge{int nxt,to,val;}e[N<<1];int fir[N];
        void add(int u,int v,int w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
        struct Beizeng{
            int fa[N][20],data[N][20];
            void init(){
                for(int i=1;i<20;++i){
                    for(int j=1;j<=n;++j){
                        fa[j][i]=fa[fa[j][i-1]][i-1];
                        data[j][i]=min(data[j][i-1],data[fa[j][i-1]][i-1]);
                    }
                }
            }
            void lca(int u,int v){
                if(tr[u].dep<tr[v].dep) swap(u,v);
                /*....*/
            }
        };
    }t,now;
    

    写到这里的时候,作者的 ( ext{vscode}) 就给我疯狂报错,说什么非静态成员引用必须与特定对象相对,错误是在函数 ( ext{lca}) 中的 ( ext{tr}) ,然后我就很迷惑,就上网 ( ext{google}) 了一下,意识到了问题的关键。

    他报错的原因是因为我在结构体里面的函数调用的 tr 是一个不确定的变量,里面的结构体 Beizeng 无法得知他外面的所套的 Tree 到底是哪一个,所以针对这个问题,我们就可以有了一个解决方案。

    我们可以在 Beizeng 里面开一个指针,然后在定义的时候就将这个指针 Tr 指向外面的 Tree 就可以直接调用了。

    然后这个问题就解决了,但是我想要的空间复杂度上的优化并没有实现。

    进阶

    然后发现,我们实际上可以开两个结构体,相应的要不要写倍增,给倍增开空间就可以解决了,但是这样的话外面就需要写两棵树,没有达到代码复杂度降低的目的。

    然后我们就可以引入一个叫做继承的概念。就是一个子类可以继承父类的特性,同时可以满足拥有自己的特性,写下来大致是这样。

    struct TreeBase {
    	struct Node{int dep;}tr[N];
    	struct Edge{int nxt,to,val;}e[N<<1];int fir[N];
    	void add(int u,int v,int w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
    };
    struct TreeWithBeizeng:virtual public TreeBase {
    	int fa[N][20],data[N][20];
    	void init(){
    		for(int i=1;i<20;++i){
    			for(int j=1;j<=n;++j){
    				fa[j][i]=fa[fa[j][i-1]][i-1];
    				data[j][i]=min(data[j][i-1],data[fa[j][i-1]][i-1]);
    			}
    		}
    	}
    	void lca(int u,int v){
    		if(tr[u].dep < tr[v].dep) swap(u,v);
    	}
    };
    

    其中继承有三个参数:

    • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。
    • 保护继承(protected): 当一个类派生自保护基类时,基类的公有保护成员将成为派生类的保护成员。
    • 私有继承(private):当一个类派生自私有基类时,基类的公有保护成员将成为派生类的私有成员。

    然后其中的 virtual 实际上是一种判重机制,就是如果一个类继承了多次同一个类,写了 virtual 的类会被只记录一次。

    具体的可以去这里学习一下。

    然后我们这里就是利用这个继承的特性,然后对应需要的树就开对应的结构体,然后同时节约了空间且简短了代码。

    我突然觉得这个博客有点蠢看起来。

    拓展

    同时的是,作者还在其中学习了关于构造函数的一些问题。

    比如我们的初始化 a=1 实际上他是这样的 a(1)

    这就是为什么 const int a=1; 是可行的,但是 const int a;a=1; 是不可行的了。

    因为实际上 const 类型是只接受构造函数的修改的。

    然后对于构造函数,他也有两种写法

    struct PK_is_a_Fool{
    	const int IQ;
    	PK_is_a_Fool(int a):IQ(a){}
    	PK_is_a_Fool(int a){IQ=a;}
    };
    

    但是两者是有区别的,区别和 const 对于两者的区别一样但是注意,第一种不能这么写

    struct PK_is_a_Fool{
    	const int IQ;
    	PK_is_a_Fool(int a){IQ(a);}
    };
    

    这个是错的。

    果然这个博客很傻逼,写得都是别人知道的东西。

    神的代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+5;
    int n;
    
    struct TreeBase {
    	struct Node{int dep;}tr[N];
    	struct Edge{int nxt,to,val;}e[N<<1];int fir[N];
    	void add(int u,int v,int w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
    };
    
    struct TreeWithBeizeng : virtual public TreeBase {
    	int fa[N][20],data[N][20];
    	void init(){
    		for(int i=1;i<20;++i){
    			for(int j=1;j<=n;++j){
    				fa[j][i]=fa[fa[j][i-1]][i-1];
    				data[j][i]=min(data[j][i-1],data[fa[j][i-1]][i-1]);
    			}
    		}
    	}
    	void lca(int u,int v){
    		if(tr[u].dep < tr[v].dep) swap(u,v);
    	}
    };
    
    struct TreeWithSP : virtual public TreeBase {
    	/* ... */
    	/*
    	struct Foo : public bar;
    	int a : 2;
    	Foo(int a) : a(a);
    	int a = flag ? 1 : 0; 
    	for (int v: e[u]) {	}
    	for (auto v_iterator = e[u].begin(), v; _iterator != e[u].end(); ++v_iterator) {
    		v = *v_iterator;
    	}
    	*/
    	int weight_son[N];
    	void modify(int u, int v) {
    		printf("modify(%d, %d)
    ", u, v);
    	}
    };
    
    struct Tree : public TreeBase, public TreeWithBeizeng, public TreeWithSP {}
    t,now;
    
    int main(){
    	t.modify(1,2);
    	t.init();
    	t.lca(1,2);
    }
    
  • 相关阅读:
    BZOJ3752 : Hack
    XIV Open Cup named after E.V. Pankratiev. GP of SPb
    XIII Open Cup named after E.V. Pankratiev. GP of Ukraine
    BZOJ2087 : [Poi2010]Sheep
    BZOJ2080 : [Poi2010]Railway
    BZOJ2082 : [Poi2010]Divine divisor
    Moscow Pre-Finals Workshop 2016. National Taiwan U Selection
    XIII Open Cup named after E.V. Pankratiev. GP of Asia and South Caucasus
    XIII Open Cup named after E.V. Pankratiev. GP of Azov Sea
    XIII Open Cup named after E.V. Pankratiev. GP of SPb
  • 原文地址:https://www.cnblogs.com/Point-King/p/14593618.html
Copyright © 2011-2022 走看看