题目背景

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

题目内容

喵星系有\(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. 简单编程:如何用java来打印出一个5行的三角形

  2. Django-配置镜像源,虚拟环境详解

    0.配源虚拟 全局配源 阿里云镜像站:https://developer.aliyun.com/mirror/ # 在cmd中操作,查找文件 C:\Users\Administrator>pip ...

  3. 使用App Metrics实现性能监控

    App Metrics监控需要安装InfluxDB时序数据库和Grafana可视化分析工具 1.安装InfluxDB 下载地址:https://portal.influxdata.com/downlo ...

  4. hadoop和hbase高可用模式部署

    记录apache版本的hadoop和hbase的安装,并启用高可用模式. 1. 主机环境 我这里使用的操作系统是centos 6.5,安装在vmware上,共三台. 主机名 IP 操作系统 用户名 安 ...

  5. 将HBase中的表加载到hive中

    两种方式加载hbase中的表到hive中,一是hive创建外部表关联hbase表数据,二是hive创建普通表将hbase的数据加载到本地 1. 创建外部表 hbase中已经有了一个test表,内容如下 ...

  6. 关于AES的S盒子的计算

    对于置换piS 有如下计算: The matrix used in AES is a rotational matrix based on the value 0x1F, which is 00011 ...

  7. 将lua编译进nginx

    1.先安装lua-jit,网上说也可以下载lua,不过lua-jit效率比较高,地址:http://luajit.org/download.html我下的是2.0.3版本的下载解压后,直接make & ...

  8. java 多线程-4

    十四.sleep方法和wait方法的区别 [面试题] 相同点: 一旦执行方法,都可以使得当前线程进入阻塞状态. 不同点: 两个方法的声明位置不同:Thread类声明sleep():Object类中声明 ...

  9. 虚拟机Ubuntu(18.04.2)下安装配置Hadoop(2.9.2)(伪分布式+Java8)

    [本文结构] [1]安装Hadoop前的准备工作 [1.1] 创建新用户 [1.2] 更新APT [1.3] 安装SSH [1.4] 安装Java环境 [2]安装和配置hadoop [2.1] Had ...

  10. python类中的__init__和__new__方法

    Python中类: Python中在创建类的过程中最先调用的不是__init__方法而是__new__方法,__new__方法是一个静态方法,在创建一个类对象时其实是通过__new__方法首先创建出一 ...