本题已收录至2019/9/15 本周总结

题目

【问题描述】

Hja有一棵\(n\)个点的树,树上每个点有点权,每条边有颜色.一条路径的权值是这条路径上所有点的点权和,一条合法的路径需要满足该路径上任意相邻的两条边颜色都不相同.问这棵树上所有合法路径的权值和是多少.

【输入格式】

第一行一个数\(n\).

接下来一行\(n\)个数代表每个点的权值.

接下来\(n-1\)行每行三个整数\(u\ v\ c\),代表\(u\)到\(v\)之间有一条颜色为\(c\)的边。

【输出格式】

一行一个整数代表答案.

【样例输入】

6
6 2 3 7 1 4
1 2 1
1 3 2
1 4 3
2 5 1
2 6 2

【样例输出】

134

【数据范围与规定】

\(1\le n \le 3\times 10^5,1\le c \le 10^9\)

保证答案小于\(2^{63}-1\).

思路1 \(O(n^2)\)暴力

从每个点DFS一次,计算出以该点为端点的所有合法路径的权值和.每条链被计算了两次,因此答案要\(/2\).

时间复杂度\(O(n^2)\)

#include<bits/stdc++.h>
#define LL long long
const int SIZE=300005;
int n,head[SIZE],nex[SIZE*2],to[SIZE*2],edge[SIZE*2],Tot;
LL weight[SIZE],Ans; void Link(int u,int v,int e)
{
nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;edge[Tot]=e;
nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;edge[Tot]=e;
} void DFS(int u,int F,LL sum,int Las)
{
Ans+=sum;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(v==F||edge[i]==Las)continue;//不能出现连续两条颜色相同的边
DFS(v,u,sum+weight[v],edge[i]);
}
} int main()
{
int u,v,e;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&weight[i]);
Ans-=weight[i];
}
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&e);
Link(u,v,e);
}
for(int i=1;i<=n;i++)DFS(i,0,weight[i],0);
printf("%lld",Ans/2);
return 0;
}

思路2 \(O(n^2)\)记忆化搜索

容易发现,思路1的搜索有大量的重复状态.

举个例子,

现在我们从涂黑的点开始搜索,在粉框里搜出来了4种状态,列举在了右边.

需要注意的是,有一种状态(粉框左下角的连续两条蓝边)是不合法的,我们没有保留这种状态.

现在我们换第二个点进行DFS,就拿上图中涂黑的点来举例吧.

从这个点开始搜索,依然会进入粉框,在粉框中依然会搜出这4种状态,列举在了右边.

容易发现,这4种状态和上一个点的状态基本一样,只有绿框中的不一样.

换句话说,这4种状态在粉框中的部分是一样的.

这启示我们把粉框里搜出来的状态存起来,下次直接调用,不再在粉框内部进行DFS.

也就是这样:

当我们在搜涂黑的点时,不用进入粉框搜索了,直接取出粉框的状态,加入答案.

容易发现,状态应该存储在(有向)边上,而且这样的记忆化可以推广到所有边.

于是我们就得到了记忆化搜索的代码.

#include<bits/stdc++.h>
#define LL long long
const int SIZE=600005;
int n,head[SIZE],nex[SIZE],to[SIZE],edge[SIZE],Tot;
LL weight[SIZE],Ans; void Link(int u,int v,int e)
{
nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;edge[Tot]=e;
nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;edge[Tot]=e;
} struct Re
{
LL Res;
int w;
Re operator +(const Re &o)const
{
return (Re){Res+o.Res,w+o.w};
}
}x[SIZE];
Re DFS(int u,int F,LL sum,int Las)
{
Re R=(Re){sum,1};
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(v==F||edge[i]==Las)continue;
if(x[i].w)R=(Re){R.Res+x[i].w*sum+x[i].Res,R.w+x[i].w};
else
{
Re Tem=DFS(v,u,sum+weight[v],edge[i]);
R=R+Tem;
x[i]=(Re){Tem.Res-sum*Tem.w,Tem.w};
}
}
return R;
} int main()
{
int u,v,e;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&weight[i]);
Ans-=weight[i];
}
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&e);
Link(u,v,e);
}
for(int i=1;i<=n;i++)
Ans+=DFS(i,0,weight[i],0).Res;
printf("%lld",Ans/2);
return 0;
}

容易证明:

一条链的情况时间复杂度\(O(n)\).

随机数据时间复杂度近似\(O(n)\).

菊花图时间复杂度\(O(n^2)\)

证明略.

思路3 记忆化搜索的优化

刚刚睡觉的时候想到可以对点也记忆化,但是这样要用map,时间复杂度\(O(nlogn)\).

#include<bits/stdc++.h>
#define LL long long
const int SIZE=600005;
int n,head[SIZE],nex[SIZE],to[SIZE],edge[SIZE],Tot;
LL weight[SIZE],Ans; void Link(int u,int v,int e)
{
nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;edge[Tot]=e;
nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;edge[Tot]=e;
} struct Re
{
LL Res;int w;
Re operator +(const Re &o)const{return (Re){Res+o.Res,w+o.w};}
Re operator -(const Re &o)const{return (Re){Res-o.Res,w-o.w};}
}x[SIZE],mk[SIZE];
std::map<std::pair<int,int>,Re>mp; bool Finished[SIZE];
Re DFS(int u,int F,LL sum,int Las)
{
Re R=(Re){sum,1};
if(Finished[u])
{
Re Tem=mk[u]-mp[std::make_pair(u,Las)];
R=(Re){R.Res+Tem.w*sum+Tem.Res,R.w+Tem.w};
return R;
}
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(v==F){if(x[i].w)Finished[u]=1;continue;}
if(x[i].w)
{
if(edge[i]!=Las)R=(Re){R.Res+x[i].w*sum+x[i].Res,R.w+x[i].w};
continue;
}
Re Tem=DFS(v,u,sum+weight[v],edge[i]);
if(edge[i]!=Las)R=R+Tem;
x[i]=(Re){Tem.Res-sum*Tem.w,Tem.w};
mp[std::make_pair(u,edge[i])]=mp[std::make_pair(u,edge[i])]+x[i];
mk[u]=mk[u]+x[i];
}
return R;
} int main()
{
int u,v,e;
scanf("%d",&n);
for(int i=1;i<=n;++i){scanf("%lld",&weight[i]);Ans-=weight[i];}
for(int i=1;i<n;++i){scanf("%d%d%d",&u,&v,&e);Link(u,v,e);}
for(int i=1;i<=n;++i)Ans+=DFS(i,0,weight[i],0).Res;
printf("%lld",Ans/2);
return 0;
}

b 解题报告的更多相关文章

  1. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  2. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  3. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  4. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  5. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

  6. 习题: codevs 2492 上帝造题的七分钟2 解题报告

    这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...

  7. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

  8. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  9. LeetCode 解题报告索引

    最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中......                        ...

  10. ACM: Just a Hook 解题报告 -线段树

    E - Just a Hook Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u   D ...

随机推荐

  1. 洛谷P1551 亲戚

    洛谷P1551 亲戚 原题 题目背景 若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系. 题目描述 规定:x和y是亲戚,y和z是 ...

  2. Qt Gui 第五章绘图类

    双缓冲 void Plotter::refreshPixmap() { pixmap = QPixmap(size()); pixmap.fill(, ); QPainter painter(& ...

  3. mysql空数据的处理

    1.统计分析时,统计值为null则转为0 //统计婚姻接口调用次数select count ,(zsj/count) as pjdysjfrom(-- 实时调用量 评论返回时间差(取平均值)selec ...

  4. HTML连载64-a标签伪类选择器的注意点与练习

    一.a标签的伪类选择器注意点 (1)a标签的伪类选择器可以单独出现,也可以一起出现.也就是可以设置多个状态的样式. (2) a标签的伪类选择器如果一起出现,那么有严格的顺序要求,编写的顺序必须要遵守原 ...

  5. 树莓派4B遇到的坑

    由于大创需要用到机器学习这些东西,入手了一个树莓派4B(新手没弄过,直接上手最新版果然是有坑的),大佬勿喷

  6. 蓝桥杯第十届C组试题C

    从0开始,从右到左给这些字符串的每一位字母起个名字. 比如:A(1位)A(0位) A(2位)A(1位)A(0位) AA = 27, 可以看成(26 * 1)+ A(1) 因为:字母每经过一个轮回,可就 ...

  7. Win10安装3 —— U盘启动工具安装

    本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12364593.html 一:准备一个空U盘 U盘容量推荐至少8个G,先提前备份好 ...

  8. Day5 多线程 锁

    synchronized保证操作原子性 这是因为对变量进行读取和写入时,结果要正确,必须保证是原子操作.原子操作是指不能被中断的一个或一系列操作. 通过加锁和解锁的操作,就能保证3条指令总是在一个线程 ...

  9. 转载:dsp芯片的定点运算

    转自: http://ishare.iask.sina.com.cn/f/37179153.html

  10. C语言-浮点数的秘密

    一.浮点数的秘密 1.内存中的浮点数 浮点数在内存中的存储方式为:符号位.指数.尾数 十进制浮点数的内存表示: 实例分析: #include <stdio.h> //打印十进制的内存表示 ...