• “蔚来杯“2022牛客暑期多校训练营6 F题: Hash


    F题: Hash

    原题链接:https://ac.nowcoder.com/acm/contest/33191/F

    题目大意

    T T T 包含不超过 50 50 50 个节点,根节点为 1 1 1

    定义
    F ( T ) = ( ∑ i = 1 n ∑ j = i + 1 n X i Y j Z l c a ( i , j ) ) m o d    998244353 F(T)=(\sum_{i=1}^n\sum_{j=i+1}^nX^iY^jZ^{lca(i,j)})\mod 998244353 F(T)=(i=1nj=i+1nXiYjZlca(i,j))mod998244353

    已知 F ( T ) , X , Y , Z F(T),X,Y,Z F(T),X,Y,Z ,求 T T T

    题解

    不妨将树 T T T 分为根节点 1 1 1 与两棵子树(左子树和右子树)。
    当左子树和右子树的大小确定时,其中根与左子树,根与右子树,左子树与右子树间对哈希函数的贡献都很容易统计, 因此我们只需要考虑左子树内部和右子树内部的贡献即可即可。

    我们通过随机得到大量的可能左子树内哈希值,然后随机生成右子树,检查是否有左子树与其合并后哈希值恰好为 F ( T ) F(T) F(T)(类似于折半搜索的思想)。该算法在通常情况下的复杂度很优秀,可以通过此题。

    那么我们考虑如何快速计算一棵子树内的哈希值,以下给出一个 O ( n 2 ) O(n^2) O(n2) 的做法:
    我们按照编号从小到大枚举每个节点,对于节点 a a a ,枚举是可能是 a a a 与其他节点的 L C A LCA LCA 的位置(其实就是 a a a 到根的路径上的点,即 a a a 的祖先),计算 a a a 与编号小于 a a a 的节点对哈希函数的贡献,同时更新某种标记以便计算编号大于 a a a 的节点与 a a a 对答案的贡献。
    设节点 a a a 与节点 b b b ( a < b aa<b )的深度为 k k k 的公共祖先为 L C A ( k ) LCA(k) LCA(k) ,其中最近公共祖先为 L C A ( p ) LCA(p) LCA(p)
    (设根节点 1 1 1 的深度为 1 1 1 )
    我们可以在枚举到 a a a 时对辅助数组 s u m sum sum 进行更新:
    s u m L C A ( k ) + = { X a Z L C A ( k ) k = 2 X a Z L C A ( k ) − X a Z L C A ( k − 1 ) o t h e r w i s e sum_{LCA(k)}+=

    {XaZLCA(k)k=2XaZLCA(k)XaZLCA(k1)otherwise" role="presentation" style="position: relative;">{XaZLCA(k)k=2XaZLCA(k)XaZLCA(k1)otherwise
    sumLCA(k)+={XaZLCA(k)XaZLCA(k)XaZLCA(k1)k=2otherwise

    (第一种情况即 L C A ( k ) LCA(k) LCA(k) 的祖先为根节点 1 1 1 ,即 L C A ( k ) LCA(k) LCA(k) 为该子树的根时)
    那么在枚举到 b b b 时,对哈希函数的贡献可计算为
    Y b ∑ i = 2 p s u m L C A ( i ) Y^b\sum_{i=2}^p sum_{LCA(i)} Ybi=2psumLCA(i)

    其中与 a a a 有关的项可展开为:
    Y b ( X a Z L C A ( 2 ) + X a Z L C A ( 3 ) − X a Z L C A ( 2 ) + . . . + X a Z L C A ( p ) − X a Z L C A ( p − 1 ) ) = Y b X a Z L C A ( p )

    Yb(XaZLCA(2)+XaZLCA(3)XaZLCA(2)+...+XaZLCA(p)XaZLCA(p1))=YbXaZLCA(p)" role="presentation" style="position: relative;">Yb(XaZLCA(2)+XaZLCA(3)XaZLCA(2)+...+XaZLCA(p)XaZLCA(p1))=YbXaZLCA(p)
    =Yb(XaZLCA(2)+XaZLCA(3)XaZLCA(2)+...+XaZLCA(p)XaZLCA(p1))YbXaZLCA(p)

    与哈希函数中的形式相同,这正是我们想要的。
    最坏情况下子树退化为链,此时对于每个节点枚举祖先的复杂度为 O ( n ) O(n) O(n) ,计算子树内贡献的复杂度为 O ( n 2 ) O(n^2) O(n2)

    参考代码

    #include
    using namespace std;
    
    template<class T>inline void read(T&x){
    	char c,last=' ';
    	while(!isdigit(c=getchar()))last=c;
    	x=c^48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
    	if(last=='-')x=-x;
    }
    
    #define ll long long
    const int MAXN=55,MAXT=4e4+5,P=998244353;
    int n=50;
    ll F,X,Y,Z;
    ll pX[MAXN],pY[MAXN],pZ[MAXN];//幂表
    ll sum[MAXN];//辅助数组,用于计算哈希值
    
    int f[MAXN];//存储当前计算的子树
    int Fa[MAXT][MAXN];//用于存储左子树
    
    int stk[MAXN],top;//一个栈,用于维护祖先
    vector<int>A,B;//左/右子树
    map<int,int>mp;//记录对于一个哈希值,是否存在对应的左子树
    
    void init(){
    	pX[0]=pY[0]=pZ[0]=1;
    	for(int i=1;i<=n;++i){//对X,Y,Z的幂进行打表
    		pX[i]=pX[i-1]*X%P;
    		pY[i]=pY[i-1]*Y%P;
    		pZ[i]=pZ[i-1]*Z%P;
    	}
    	vector<int>().swap(A);
    	vector<int>().swap(B);
    	for(int i=2;i<=n/2;++i){
    		A.push_back(i);//左子树
    		F=(F-X*pY[i]%P*Z)%P;//左子树内每个点与根的贡献
    	}
    	for(int i=n/2+1;i<=n;++i){
    		B.push_back(i);//右子树
    		F=(F-X*pY[i]%P*Z)%P;//右子树内每个点与根的贡献
    	}
    	for(int i=0;i<A.size();++i){
    		for(int j=0;j<B.size();++j){
    			int x=A[i],y=B[j];
    			F=(F-pX[x]*pY[y]%P*Z)%P;//左右子树间(LCA为根节点1)的贡献
    		}
    	}
    	F=(F+P)%P;//可能为负,注意化为正数
    	mp.clear();//清空原先的左子树可能哈希值
    }
    
    void Rand(vector<int>V){//以V中节点随机生成一棵树
    	f[V[0]]=1;
    	for(int i=1;i<V.size();++i){
    		f[V[i]]=V[rand()%i];//选取一个下标小于他的作为父亲,保证最终构成一棵树
    	}
    }
    
    int Hash(vector<int>V){//计算V构成的树内部的哈希值
    	int ret=0;
    	memset(sum,0,sizeof(sum));//初始化
    	sort(V.begin(),V.end());//根据标号从小到大
    	for(int i=0,u,v;i<V.size();++i){
    		u=v=V[i],top=0;
    		while(v!=1){
    			stk[++top]=v;v=f[v];//用栈存储它的祖先们
    		}
    		int pre=0,now;
    		while(top){
    			v=stk[top--];
    			ret=(ret+pY[u]*sum[v])%P;//计算对哈希值的贡献
    			now=pX[u]*pZ[v]%P;
    			sum[v]=(sum[v]+now-pre)%P;//更新sum数组
    			pre=now;
    		}
    	}
    	return (ret+P)%P;//因为sum数组在过程中可能为负数,所以ret也可能为负数,注意化为正数
    }
    
    int main()
    {
    	int T;read(T);
    	while(T--){
    		read(F),read(X),read(Y),read(Z);
    		init();
    		for(int t=1;t<MAXT;++t){
    			Rand(A);//枚举40000次左子树
    			int val=Hash(A);
    			mp[val]=t;//记录下该棵左子树的哈希值
    			memcpy(Fa[t],f,sizeof(f));//存下这课左子树
    		}
    		while(1){
    			Rand(B);//枚举右子树
    			int val=(F-Hash(B)+P)%P;//需要的左子树哈希值
    			if(mp.find(val)!=mp.end()){//存在对应的左子树
    				int t=mp[val];
    				for(int i=2;i<=n/2;++i)f[i]=Fa[t][i];//将左子树取出
    				break;
    			}
    		}
    		cout<<n<<'\n';
    		for(int i=2;i<=n;++i)cout<<f[i]<<' '<<i<<'\n';//输出每个点的父亲到它的这条边,即输出一棵树
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
  • 相关阅读:
    BAPI 和 RFC 的区别
    安卓通讯录操作插件
    Git学习(3):Git分支操作
    C语言 在一组名字中查找指定的人名是否存在
    synchronized下的 i+=2 和 i++ i++执行结果居然不一样
    晶体管的 栅极gate 材料选用 多晶硅polysilicon,并采用 自对准工艺 self-aligned IC后端版图 【VLSI】
    撤销工作表保护密码忘记了怎么办?
    软件杯 图像识别-人脸识别与疲劳检测 - python opencv
    【luogu CF1140F】Extending Set of Points(线段树分治)
    Mybatis入门相关API
  • 原文地址:https://blog.csdn.net/Spy_Savior/article/details/126275507