小奇的仓库:换根dp

一道很好的换根dp题。考场上现场yy十分愉快

给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16

只能线性复杂度求解,m又小得奇怪。或者带一个log像kx一样打一个线段树

我们可以发现,m小的话对距离很大的路径的影响也不会超过16。

那么变化的其实就是最后4个二进制位啊。

所以我们像普通的换根dp一样求出所有距离,在额外处理一下以p为端点的全部路径里路径长度%16之后的值为k的有多少个

设为bits2[k][p]

因为换根dp的主要思路是两遍dfs,第一次处理每个点的子树,第二遍处理以这个点为端点的所有点对的状态

设子树里所有的点到当前根节点的距离是dt[p],子树所有点到当前根节点的路径%16之后值为k的路径有k个设为bits[k][p]

那么第一轮dfs转移还是很显然的,我稍微说一下第二轮。

dt[p]=dt[p]+dt[f[p]]-(dt[p]+siz[p]*vtf[p])+vtf[p]*(n-siz[p]);其中vtf[p]表示p节点到其父亲的距离

=p的子树里所有点到p的距离+所有点到p的父亲的距离-(p子树内的点到p的距离+这些点从p再到p父亲的距离)+子树p外的所有点从p父亲到p的距离

两个公式的每个部分都是对应的

那么bits数组的转移也是大同小异了(虽说稍复杂一些)

for(int j=0;j<=15;++j) bits2[j+vtf[p]&15][p]=bits[j+vtf[p]&15][p]+bits2[j][f[p]]-bits[j-vtf[p]+16&15][p];
首先是子树里本身就有bits[j+vtf[p]&15]个点满足条件
到父亲的距离为j的所有点里去除子树p里的贡献就是bits2[j][f[p]]-bits[j-vtf[p]+16&15][p]
自己想?

容易重复考虑自己到自己的距离为0而异或后不是0的情况,要减去m

具体实现细节自己想?有什么不会的评论区吧,我扔个代码就跑~

 1 //这个异或是个狗屎啊!!!但是其实异或的二进制位也不超过4,特殊处理一下?
 2 //对于每一个节点计算出子树所有点到它的距离和,再计算出有多少点到它的距离&15后是几
 3 #include<cstdio>
 4 #define int long long
 5 int n,m,cnt,fir[100005],l[200005],to[200005],v[200005],dt[100005],siz[100005];
 6 int bits[16][100005],f[100005],vtf[100005],bits2[16][100005];
 7 void dfs(int p,int fa){
 8     siz[p]=1; bits[0][p]=1; f[p]=fa;
 9     for(int i=fir[p];i;i=l[i])if(to[i]!=fa){
10         dfs(to[i],p); siz[p]+=siz[to[i]]; dt[p]+=dt[to[i]]+siz[to[i]]*v[i]; vtf[to[i]]=v[i];
11         for(int j=0;j<=15;++j) bits[j+v[i]&15][p]+=bits[j][to[i]];
12     }
13 }
14 void DFS(int p){
15     if(p!=1){
16         dt[p]+=dt[f[p]]-(dt[p]+siz[p]*vtf[p])+vtf[p]*(n-siz[p]);
17         for(int j=0;j<=15;++j) bits2[j+vtf[p]&15][p]=bits[j+vtf[p]&15][p]+bits2[j][f[p]]-bits[j-vtf[p]+16&15][p];
18     }else for(int j=0;j<=15;++j)bits2[j][1]=bits[j][1];
19     for(int i=fir[p];i;i=l[i]) if(to[i]!=f[p]) DFS(to[i]);
20 }
21 void link(int a,int b,int vv){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=vv;}
22 signed main(){//freopen("newt3.in","r",stdin);freopen("newcot3.out","w",stdout);
23     scanf("%lld%lld",&n,&m);
24     for(int i=1,x,y,ww;i<n;++i)scanf("%lld%lld%lld",&x,&y,&ww),link(x,y,ww),link(y,x,ww);
25     dfs(1,0);DFS(1);//for(int i=1;i<=n;++i)printf("%lld\n",dt[i]);
26     for(int i=1;i<=n;++i){int ans=dt[i];for(int j=0;j<=15;++j)ans+=bits2[j][i]*((j^m)-j);printf("%lld\n",ans-m);}
27 }
View Code

 

转载于:https://www.cnblogs.com/hzoi-DeepinC/p/11383336.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值