2019 ICPC银川站 E.Xor tree(位贡献 + 长链剖分维护后缀和)

题目大意:定义 p ( x , k ) : p(x,k): p(x,k)x 为根结点,深度小于等于k的所有结点个数。再定义一个集合S的运算 f ( S ) : f(S): f(S)集合内元素两两异或值的平方和,例如 S = {1,1,2,3},则 f(S) = ( 1 ⨁ 1 ) 2 (1 \bigoplus1)^2 (11)2 + ( 1 ⨁ 2 ) 2 (1 \bigoplus2)^2 (12)2 + ( 1 ⨁ 3 ) 2 (1 \bigoplus3)^2 (13)2 + ( 1 ⨁ 2 ) 2 (1 \bigoplus2)^2 (12)2 + ( 1 ⨁ 3 ) 2 (1 \bigoplus3)^2 (13)2 + ( 2 ⨁ 3 ) 2 (2 \bigoplus3)^2 (23)2

输入 n,k,给一棵n个结点的有根树,1为根结点,对树上所有结点 x,输出所有的 f ( p ( x , k ) ) f(p(x,k)) f(p(x,k))


根据式子很容易想到按位的贡献来做,单独考虑每一位的贡献,如果两个数异或起来有三个1,例如a,b,c位为 1,那么这一对数的贡献是 ( 2 a + 2 b + 2 c ) 2 (2^a + 2^b + 2^c)^2 (2a+2b+2c)2,简记为 ( a + b + c ) 2 (a + b + c)^2 (a+b+c)2

拆开后可以得到 a 2 + b 2 + c 2 + 2 a b + 2 a c + 2 b c a^2 + b^2 + c^2 + 2ab + 2ac + 2bc a2+b2+c2+2ab+2ac+2bc

一个做法是:

第一次先计算 a 2 + b 2 + c 2 a^2 + b^2 + c^2 a2+b2+c2,即单个位为1的贡献,这很容易计算,只要集合内考虑某一位为1的数字个数乘上这一位为0的数字个数再乘上这一位贡献的平方

对于 2 a b + 2 b c + 2 a c 2ab + 2bc + 2ac 2ab+2bc+2ac,需要考虑两个位,经过第一步的计算之后,第二步计算需要补加贡献:枚举两个位x,y,这两个数异或起来这两个位同时为1,那么必然漏算了 2 x y 2xy 2xy 这个贡献,统计有多少对数字或起来这两异同时为 1,然后把漏掉的贡献加上。

对于在有根树上统计某一位或某两位为1并且与深度有关的结点数,显然可以dsu on tree 或长链剖分,由于需要枚举二进制位pair,用dsu on tree 复杂度为达到 n log ⁡ 3 ( n ) n \log^3(n) nlog3(n),即使4s也可能会T飞

上长链剖分,需要维护一个形如 dp[i][j]:表示 i 为根结点,深度 ≤ \leq j 且某一位或某两位满足要求的结点个数,直接维护这个前缀和或统计这个前缀和复杂度都会达到 n 2 n^2 n2,因为必然会枚举到重儿子的链长,而长链剖分的复杂度保证就是不枚举重儿子的链长。

做法是改为维护后缀和,长链剖分可以直接维护并转移,需要的数据减一下即可。

然后再上一个更加弱智的写法,常数大到差点过不去


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
vector<int> g[maxn];
typedef unsigned long long ll;
ll tmp[maxn],*id1,*dp[maxn];
ll tmp2[maxn],*id2,*tp[maxn];
ll val[maxn];
int len[maxn],son[maxn],n,k;
ll ans[maxn];
void prework(int u,int fa) {
	len[u] = 0;son[u] = 0;
	for(int i = 0; i < g[u].size(); i++) {
		int it = g[u][i];
		if(it == fa) continue;
		prework(it,u);
		if(son[u] == 0 || len[son[u]] < len[it])
			son[u] = it;	
	}
	len[u] = len[son[u]] + 1;
}
void dfs(int u,int fa,int p) {
	dp[u][0] = ((val[u] >> p) & 1);
	tp[u][0] = !dp[u][0];
	if(son[u]) {
		tp[son[u]] = tp[u] + 1;
		dp[son[u]] = dp[u] + 1;
		dfs(son[u],u,p);
	}
	for(int i = 0; i < g[u].size(); i++) {
		int it = g[u][i];
		if(it == fa || it == son[u]) continue;
		dp[it] = id1; id1 += len[it];
		tp[it] = id2; id2 += len[it];
		dfs(it,u,p);
		for(int i = 0; i < len[it]; i++) {
			tp[u][i + 1] += tp[it][i];
			dp[u][i + 1] += dp[it][i];
		}
	}
	if(len[u] - 1 >= 1) {
		dp[u][0] += dp[u][1];
		tp[u][0] += tp[u][1];
	}
	ll a = dp[u][0],b = tp[u][0];
	if(k + 1 <= len[u] - 1) {
		a -= dp[u][k + 1];
		b -= tp[u][k + 1];
	} 
	ans[u] += a * b * (1llu << p) * (1llu << p);
}
void dfs2(int u,int fa,int p,int q) {
	dp[u][0] = ((val[u] >> p) & 1) && (val[u] >> q & 1);
	tp[u][0] = !((val[u] >> p) & 1) && !(val[u] >> q & 1);
	if(son[u]) {
		tp[son[u]] = tp[u] + 1;
		dp[son[u]] = dp[u] + 1;
		dfs2(son[u],u,p,q);
	}
	for(int i = 0; i < g[u].size(); i++) {
		int it = g[u][i];
		if(it == fa || it == son[u]) continue;
		dp[it] = id1; id1 += len[it];
		tp[it] = id2; id2 += len[it];
		dfs2(it,u,p,q);
		for(int i = 0; i < len[it]; i++) {
			tp[u][i + 1] += tp[it][i];
			dp[u][i + 1] += dp[it][i];
		}
	}
	if(len[u] - 1 >= 1) {
		dp[u][0] += dp[u][1];
		tp[u][0] += tp[u][1];
	}
	ll a = dp[u][0],b = tp[u][0];
	if(k + 1 <= len[u] - 1) {
		a -= dp[u][k + 1];
		b -= tp[u][k + 1];
	} 
	ans[u] += 2 * a * b * (1llu << p) * (1llu << q);
}
void dfs3(int u,int fa,int p,int q) {
	dp[u][0] = (((val[u] >> p) & 1) == 1) && ((val[u] >> q & 1) == 0);
	tp[u][0] = (((val[u] >> p) & 1) == 0) && ((val[u] >> q & 1) == 1);
	if(son[u]) {
		tp[son[u]] = tp[u] + 1;
		dp[son[u]] = dp[u] + 1;
		dfs3(son[u],u,p,q);
	}
	for(int i = 0; i < g[u].size(); i++) {
		int it = g[u][i];
		if(it == fa || it == son[u]) continue;
		dp[it] = id1; id1 += len[it];
		tp[it] = id2; id2 += len[it];
		dfs3(it,u,p,q);
		for(int i = 0; i < len[it]; i++) {
			tp[u][i + 1] += tp[it][i];
			dp[u][i + 1] += dp[it][i];
		}
	}
	if(len[u] - 1 >= 1) {
		dp[u][0] += dp[u][1];
		tp[u][0] += tp[u][1];
	}
	ll a = dp[u][0],b = tp[u][0];
	if(k + 1 <= len[u] - 1) {
		a -= dp[u][k + 1];
		b -= tp[u][k + 1];
	} 

	ans[u] += 2 * a * b * (1llu << p) * (1llu << q);
}
int main() {
	scanf("%d%d",&n,&k);
	for(int i = 1; i <= n; i++) {
		scanf("%llu",&val[i]);
	}
	for(int i = 2,f; i <= n; i++) {
		scanf("%d",&f);
		g[f].push_back(i);
		g[i].push_back(f);
	}
	prework(1,0);
	for(int j = 0; j <= 29; j++) {
		id1 = tmp,dp[1] = id1,id1 += len[1];
		id2 = tmp2,tp[1] = id2,id2 += len[1];
		dfs(1,0,j);
	}
	for(int i = 0; i <= 29; i++) {
		for(int j = i + 1; j <= 29; j++) {
			id1 = tmp,dp[1] = id1,id1 += len[1];
			id2 = tmp2,tp[1] = id2,id2 += len[1];
			dfs2(1,0,i,j);			
		}
	}
	for(int i = 0; i <= 29; i++) {
		for(int j = i + 1; j <= 29; j++) {
			id1 = tmp,dp[1] = id1,id1 += len[1];
			id2 = tmp2,tp[1] = id2,id2 += len[1];
			dfs3(1,0,i,j);			
		}
	}
	for(int i = 1; i <= n; i++) {
		printf("%llu\n",ans[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值