题目背景

小奇采的矿实在太多了,它准备在喵星系建个矿石仓库。令它无语的是,喵星系的货运飞船引擎还停留在上元时代!

题目内容

喵星系有\(n\)个星球,星球以及星球间的航线形成一棵树。

从星球\(a\)到星球\(b\)要花费\([\text{dis}(a,b)\ \text{xor}\ M]\)秒。(\(\text{dis}(a,b)\)表示ab间的航线长度,\(\text{xor}\)为位运算中的异或)

为了给仓库选址,小奇想知道,星球\(i(1\leq i\leq n)\)到其它所有星球花费的时间之和。

数据范围

\(6\leq n\leq 100000,0\leq M\leq 15\)

思路

出题人:

算法1:

不会写函数的小伙伴们,我们只需要写个floyd,就有10分啦!

算法2:

在算法1的基础上,我们对每条边处理一下xor,就有20分啦!

算法3:

简单的树形DP,或者你会nlogn的dij,处理完每个点到其它点的最短路后再加上xor,那么这样就有30分啦!

算法4:

第4、5个点无需xor,那么我们树形DP扫一个节点与其它所有节点的路径长度之和,可以合并信息,最终均摊O(1),50分到手。

算法5:

第6个点xor 1,那么我们树形DP到一个点时记录有多少个0,多少个1,然后每当一条路径到2,那部分就再记录一个值,60分到手。

算法6:

如果你第6个点都过了,却没有满分,笨死啦!

一样的嘛,就是原来的“0”、“1”、大于等于2变成了0~16么~~

满了。

我:?

考场上直接打的\(O(n^2)\)枚举区间再加上求\(\text{LCA}\)的复杂度的暴力,结果一时脑瘫建边的时候就异或了\(M\)结果惨挂\(10pts\),然后考后改成最后再异或就\(30pts\)了...(差点有比郭神高的机会呢qwq)

然后正解是换根\(dp\),又是假期埋下的一个坑吗


先考虑没有异或的情况。设已经搜到了边\(<u,v,w>\),且\(u\)是\(v\)的父亲,那么如何更新\(ans[v]\)呢?

\[ans[v]=ans[u]+(n-size[v])\times w-size[v]\times w
\]

当然你会选择合并同类项,不过先不合并比较好理解,对于\(u\)以上的节点,其个数为\(n-size[v]\),对于原来的\(ans[u]\)距离多了一个\(w\),所以加上\((n-size[v])\times w\),对于\(v\)的子树节点,对\(v\)的距离就是其到\(u\)的距离减去\(w\),所以就能得到以上的柿子。

大概这个样子:

然而本题要求异或,由我惨挂10分的经历可以知道异或并不满足分配律,所以并不能边加边异或。然而可以看出\(M\leq 15\),转换为二进制为\(1111\),所以最后异或\(M\)的时候仅会对后四位有影响,所以只需要记录后四位的状态即可。

设\(f[i][j]\)表示到了\(i\)点,当前后四位的状态为\(j\),能伸展出的路径条数。

对于初始:\(f[u][0]=1\),表示自己到自己为一条路径。为了方便,你可以先加上自己然后最后减去。

然后就是\(f[u][(j+w)\ \%\ 16]+=f[v][j]\),从子树转移过来。

然而除了子树以外还有别的点,如何转移呢?

\[f[v][(j+w)\ \%\ 16]+=(f[u][j]-f[v][(j-w)\ \%\ 16])
\]

很容易理解,对于\(v\)的父亲\(u\),先刨去子树\(v\)的贡献,然后剩下的就是其他点到\(u\)的贡献,你再通过\(<u,v,w>\)边转移到\(v\)上,再加上原来就有的\(v\)的子树的,就是整棵树到\(v\)的贡献。

最后你再异或个\(M\)即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,M;
long long f[maxn][20],ans[maxn],a[20]; struct Edge{
int from,to,w,nxt;
}e[maxn<<1]; inline int read(){
int x=0,fopt=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')fopt=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+ch-48;
ch=getchar();
}
return x*fopt;
} int head[maxn],cnt;
inline void add(int u,int v,int w){
e[++cnt].from=u;
e[cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
} void dfs1(int u,int fa){
f[u][0]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs1(v,u);
ans[u]+=ans[v];
for(int j=0;j<=15;j++){
int w=e[i].w;
f[u][(j+w)%16]+=f[v][j];
ans[u]+=f[v][j]*w;
}
}
} void dfs2(int u,int fa){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w;
if(v==fa)continue;
memset(a,0,sizeof(a));//临时先开个数组存一下,因为下面还要加siz,最好不要直接更新
int siz=0;
for(int j=0;j<=15;j++){
a[(j+w)%16]+=f[u][j]-f[v][((j-w)%16+16)%16];//防止下标变负
siz+=f[v][j];
}
ans[v]=ans[u]+(n-2*siz)*w;
for(int j=0;j<=15;j++)
f[v][j]+=a[j];
dfs2(v,u);
}
} int main(){
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
n=read();M=read();
for(int i=1;i<n;i++){
int u=read(),v=read(),w=read();
add(u,v,w);
add(v,u,w);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++){
f[i][0]--;//刨去到自己的路径
for(int j=0;j<=15;j++)
ans[i]+=((j^M)-j)*f[i][j];//加上异或后相差的值,另外还是老问题异或的优先级
printf("%lld\n",ans[i]);
}
return 0;
}

【换根DP】小奇的仓库的更多相关文章

  1. 小奇的仓库:换根dp

    一道很好的换根dp题.考场上现场yy十分愉快 给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16 只能线性复杂度求解,m又小得奇怪.或者带一个log像kx一样打一 ...

  2. 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市

    P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...

  3. 洛谷 P4284 [SHOI2014]概率充电器 概率与期望+换根DP

    洛谷 P4284 [SHOI2014]概率充电器 概率与期望+换根DP 题目描述 著名的电子产品品牌\(SHOI\) 刚刚发布了引领世界潮流的下一代电子产品-- 概率充电器: "采用全新纳米 ...

  4. [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]

    题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...

  5. 2018.10.15 NOIP训练 水流成河(换根dp)

    传送门 换根dp入门题. 貌似李煜东的书上讲过? 不记得了. 先推出以1为根时的答案. 然后考虑向儿子转移. 我们记f[p]f[p]f[p]表示原树中以ppp为根的子树的答案. g[p]g[p]g[p ...

  6. 国家集训队 Crash 的文明世界(第二类斯特林数+换根dp)

    题意 ​ 题目链接:https://www.luogu.org/problem/P4827 ​ 给定一棵 \(n\) 个节点的树和一个常数 \(k\) ,对于树上的每一个节点 \(i\) ,求出 \( ...

  7. Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)

    题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...

  8. bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp

    题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数. 期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 ...

  9. codeforces1156D 0-1-Tree 换根dp

    题目传送门 题意: 给定一棵n个点的边权为0或1的树,一条合法的路径(x,y)(x≠y)满足,从x走到y,一旦经过边权为1的边,就不能再经过边权为0的边,求有多少边满足条件? 思路: 首先,这道题也可 ...

随机推荐

  1. Webpack 打包优化之体积篇

    谈及如今欣欣向荣的前端圈,不仅有各类框架百花齐放,如Vue, React, Angular等等,就打包工具而言,发展也是如火如荼,百家争鸣:从早期的王者Browserify, Grunt,到后来赢得宝 ...

  2. windows-android-appium环境搭建

    一.安装jdk 安装jdk1.7以上版本,会生成一个jdk目录,和单独的jre目录(注意:不是jdk里面的jre,时安装过程中设置的那个jre路径)安装完成后并配置环境变量 在系统环境变量中,新建:J ...

  3. [算法]体积不小于V的情况下的最小价值(0-1背包)

    题目 0-1背包问题,问要求体积不小于V的情况下的最小价值是多少. 相关 转移方程很容易想,初始化的处理还不够熟练,可能还可以更简明. 使用一维dp数组. 代码 import java.util.Sc ...

  4. C语言汇总2

    (10-15) 注释:1.单行注释可以嵌套单行注释 eg .//lalalal//lalalal(/后面都是注释完的) 2.多行注释可以嵌套单行注释 (两个**之间的都是注释的) 3.单行注释可以嵌套 ...

  5. Object类:又回到最初的起点

    Object类大概是每个JAVA程序员认识的第一个类,因为它是所有其他类的祖先类.在JAVA单根继承的体系下,这个类中的每个方法都显得尤为重要,因为每个类都能够调用或者重写这些方法.当你JAVA学到一 ...

  6. 分布式服务(RPC)+分布式消息队列(MQ)面试题精选

    ​ 分布式系统(distributed system)是建立在网络之上的软件系统.正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性.因此,网络和分布式系统之间的区别更多的在于高层软件(特别是 ...

  7. 记一次Java获取本地摄像头(基于OpenCV)

    OpenCV官网下载地址(下载安装后,在安装目录可以找到动态链接库和OpenCv.jar) https://opencv.org/releases/ 安装完成后,这是我的安装目录 maven 依赖(这 ...

  8. vue学习09 图片切换

    目录 vue学习09 图片切换 定义图片数组:imgList:[],列表数据使用数组保存 添加图片索引:index 绑定src属性:使用v-bind,v-bind指令可以设置元素属性,比如src 图片 ...

  9. java中的几种基础排序

    import java.util.Random;import java.util.Arrays; public class Puppy {     public static void main(St ...

  10. 什么是Python???

    1.python是一种解释型语言,这就是说python不用像C语言或者C的衍生语言那样在执行前进行编译. 2.Python是一种动态类型的语言,就是python支持x = 111或者x="1 ...