GalaxyOJ-510 (点分治)
来源:互联网 发布:流程优化岗面试题 编辑:程序博客网 时间:2024/04/29 18:59
题目
链接 https://vijos.org/d/hehepig/p/58f5d7f8d3d8a10adc823e6e
Description
给一棵
n
个节点的无根树,每个节点有个非负整点权,定义一条路径的价值为路径上的(点权和)-(点权最大值)
给定参数
p
,求有多少条不同的简单路径满足它的价值恰好为p
的倍数。注意,单点也算做一个路径,
u!=v
时,u->v
和v->u
算作一条路径。简单路径就是不经过重复点的一条路径。
n⩽105p⩽107v[i]⩽109 Input
第一行包含两个数 n,p;
接下来n-1
行,每行两个数表示那两个点之间有一条树边;
接下来一行n
个整数 (v[1]~v[n]),表示点 i 的权值.Outpug
就一个数,表示答案
Sample Input
5 2
1 2
1 3
2 4
3 5
1 3 3 1 2Sample Output
9
Hint
满足条件的路径有:
(1,1),(2,2),(3,3),(4,4),(5,5),(1,4),(2,3),(2,5),(3,5)
分析
- 数据庞大,所以考虑点分治
- 基本过程和模板题差不多,不过关于减去最大点权这一点,我们应该稍作处理。
- 可以每次求 dis[] 时,把 mx[] 也顺便求出来(这条路径中最大点权),之后以 mx[] 排个序,之后就能 O(n) 处理出当前子树的答案了。(具体可以看一看程序中的一小段注释)
- 注意,求 dis[] 的时候就应该先
%p
。 - 还有一点,单点也算是一条路径,而显然单点路径的价值为0,那么肯定是可以有贡献的,于是最后输出
ans+n
。
程序
#include <cstdio>#include <algorithm>#define Mn 100005#define Mv 10000005#define add(x,y) (to[++num]=head[x],head[x]=num,edge[num]=y)#define For(x) for (int h=head[x],o=edge[h]; h; o=edge[h=to[h]])using namespace std;int n,p,ans;int edge[2*Mn+5],to[2*Mn+5],head[2*Mn+5],del[Mn+5],num;int R,cnt,mins,v[Mn+5],dis[Mn+5],siz[Mn+5],mx[Mn+5],com[Mn+5],bin[Mv+5],daan;/*部分变量说明 del[i] 节点 i 还在不在(每次分治完都把重心去掉了,免得又dfs回到上面) dis[] 从重心到子树中各个节点的路径价值的集合 mx[] dis[]中各个路径对应的最大点权值 bin[i] 一个容器,存储到当前 dis[] 为 i 的路径个数(具体用法可以看看程序) com[] 辅助排序(看看cmp应该就能懂了) siz[i] 当前重心下以 x 为根节点的子树的大小 */int mo(int x){ int kkk=x%p; return (kkk<0 ? kkk+p:kkk);}void Input(){ scanf("%d%d",&n,&p); for (int i=1,o1,o2; i<n; i++) scanf("%d%d",&o1,&o2),add(o1,o2),add(o2,o1); for (int i=0; i<n;) scanf("%d",&v[++i]); return;}int get_siz(int x,int fa){ siz[x]=1; For (x) if (o!=fa && !del[o]) siz[x]+=get_siz(o,x); return siz[x];}void dfs_R(int x,int tot,int fa){ //求重心(比较 x 变为中心的话分出的子树中大小最大值) int mxs=tot-siz[x]; For(x) if (o!=fa && !del[o]){ mxs=max(mxs,siz[o]); dfs_R(o,tot,x); } if (mxs<mins) mins=mxs,R=x;}void get_dis(int x,int tot,int lmx,int fa){ //求子树中每个点到重心这条路径的价值 dis[++cnt]=tot%=p;//-----------------!!!!!就是这里我忘记 %,调了一中午 T^T mx[cnt]=lmx; For(x) if (!del[o] && o!=fa) get_dis(o,tot+v[o],max(lmx,v[o]),x);}bool cmp(int x,int y){return mx[x]<mx[y];}int work(int x,int r){ //处理当前中心下两端点都在以 x 为根节点的子树中经过重心的符合路径个数(先不管是不是"简单路径") cnt=daan=0; if (x==r) get_dis(x,v[x],v[x],0); else get_dis(x,v[x]+v[r],max(v[x],v[r]),r); for (int i=1; i<=cnt; i++) com[i]=i; sort(com+1,com+cnt+1,cmp); for (int i=1,I=com[i]; i<=cnt; I=com[++i]){ //dis[I]+dis[j]-v[r]-mx[I] == 0 (%p) //dis[j] == v[r]+mx[I]-dis[I] (%p) //大概意思就是一个端点为 I ,另一个端点为 mx[] 小于等于 mx[I](即已经加入bin[]中的端点) daan+=bin[mo(mx[I]+v[r]-dis[I])]; bin[dis[I]]++; } for (int i=1; i<=cnt; i++) bin[dis[i]]=0; return daan;}void dfs(int x){ //分治总过程 cnt=0; mins=Mn+1; get_siz(x,0); dfs_R(x,siz[x],0); int rr=R; ans+=work(rr,rr); del[rr]=1; For(rr) if (!del[o]){ ans-=work(o,rr); dfs(o); }}int main(){ Input(); dfs(1); printf("%lld",ans+n);}
提示
- 这是我做的第二道点分题,还是有点不熟练,起先一直 RE,调了一中午才发现原来是在 get_dis() 那里我的 tot 并没有
%
,于是数据一大,tot 就会溢出int
,导致答案错误了。 - 可以先看看我打的第一道点分治题(模板题): 传送门
- 大家加油呀~
1 0
- GalaxyOJ-510 (点分治)
- GalaxyOJ-789 (分治+弗洛伊德)
- GalaxyOJ-37 (Tarjan+缩点)
- GalaxyOJ-1000 (缩点+最短路)
- 树分治-点分治
- 点分治
- 点分治
- 点分治
- 点分治
- 点分治
- 点分治。。。。。
- 点分治
- 点分治
- 点分治
- 点分治
- 点分治
- gdfzoj #510 树上路径(点分治)
- 树的分治-点分治
- Spring容器的初始化4种方式
- Hinton Neural Network课程笔记11b: 利用Hopfield Net进行信息存储
- 深入理解Java:注解(Annotation)--注解处理器
- 盒子模型
- Java设计模式--抽象工厂模式
- GalaxyOJ-510 (点分治)
- 我学tensorflow遇到的问题【总结】
- C++ 智能指针(下)
- ninePacth(.9.png) iOS 的实现
- 深入理解Java:注解(Annotation)基本概念
- 今儿开始学算法,希望在此与c友共勉!
- 色彩颜色及可选颜色使用分析
- python读取csv文件
- python库系列——json库