题目背景

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

题目内容

喵星系有\(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. CSS特效(一)

    三角形 <!-- log --> <div class="tri"></div> <style> .tri { width: 0; ...

  2. 说说ERP软件的系统设计--开源软件诞生8

    赤龙ERP系统设计篇--第8篇 用日志记录"开源软件"的诞生 赤龙 ERP 开源地址: 点亮星标,感谢支持,与开发者交流 kzca2000 码云:https://gitee.com ...

  3. hexo博客front-matter格式

    通用格式 Front-matter 是文件最上方以 --- 分隔的区域,用于指定个别文件的变量,举例来说: --- title: Hello World date: 2013/7/13 20:46:2 ...

  4. 关于while (~scanf("%d %d", &m, &n))的用法

    其功能是循环从输入流读入m和n,直到遇到EOF,有如下关系: while (~scanf("%d %d", &m, &n)) ↔ while (scanf(&quo ...

  5. JS数据类型及常用操作

    1.字符串 2.数字类型 3.布尔类型 4.数组类型 5.字典

  6. 线上问题排查-HBase写数据出现NotServingRegionException(Region ... is not online)异常

    今天线上遇到一个问题:有一台服务器的cpu持续冲高,排查发现是我们的一个java应用进程造成的,该进程在向hbase中写入数据时,日志不断地打印下面的异常: org.apache.hadoop.hba ...

  7. Spring使用@Async实现异步

    使用场景 在实际项目中,一个接口如果需要处理很多数据,如果是同步执行,通过网络请求接口可能会出现请求超时.这时候就需要使用异步执行处理了. 使用经验 代码 异步服务类 @Service // Spri ...

  8. (专题一)03 matlab变量及其操作

    给内存单元取名字就可以访问内存单元 变量的命名:变量名区分大小写 标准函数名以及命名方式必须用小写字母 matlab赋值语句有两种表达式 变量的管理       1.预定义变量  ans 是默认赋值变 ...

  9. java事件触发

    工作遇到一个问题:用netty实现服务和设备的交互,服务发送了一组指令,需要再等待时间内获取结果,如果结果提前全部返回,就进一步处理,如果等待时间内没有全部返回,就视为失败处理. 这个场景我遇到的困难 ...

  10. dubbo学习(六)dubbo管理控制台

    管理控制台的安装与使用 下载地址:https://github.com/apache/dubbo-admin/tree/master(包含管理控制台和监控中心) PS:  下载前要选择master分支 ...