bzoj3697(点分治)
来源:互联网 发布:淘宝一元拍是真的吗 编辑:程序博客网 时间:2024/04/30 15:30
采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。
还是点分治,这道题就没有那么裸的点分治了。
本题可以考虑树的点分治。问题就变成求过根满足条件的路径数。
路径上的休息站一定是在起点到根的路径上,或者根到终点的路径上。
如何判断一条从根出发的路径是否包含休息站?只要在dfs中记录下这条路径的和x,同时用个标志数组判断这条路径是否存在前缀和为x的节点。(这个判断是否有休息站,是否有一段路径权值和为0的方法,就是这个标志数组非常巧妙)
这样我们枚举根节点的每个子树。用f[i][0…1],g[i][0…1]分别表示前面几个子树以及当前子树和为i的路径数目,0和1用于区分路径上是否存在前缀和为i的节点。那么当前子树的贡献就是f[0][0] * g[0][0] + Σf [i][0] * g [-i][1] + f[i][1] * g[-i][0] + f[i][1] * g[-i][1],其中i的范围[-d,d],d为当前子树的深度。
#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<cstdlib>using namespace std;typedef long long ll;const int N=100005;const int inf=0x3f3f3f3f;int n,mi,sum,rt,depth;int size[N];bool vis[N];int head[N],tot;struct aa{int to,pre,dis;}edge[N*2];void addedge(int x,int y,int z){edge[++tot].to=y;edge[tot].pre=head[x];head[x]=tot;edge[tot].dis= z ? 1:-1;}void getsize(int u,int fa){size[u]=1;for (int i=head[u];i;i=edge[i].pre)if (!vis[edge[i].to]&&edge[i].to!=fa) {getsize(edge[i].to,u);size[u]+=size[edge[i].to];}}void getrt(int u,int fa){int tmp=sum-size[u];for (int i=head[u];i;i=edge[i].pre)if (edge[i].to!=fa&&!vis[edge[i].to]){getrt(edge[i].to,u);tmp=max(tmp,size[edge[i].to]);}if (tmp<mi) mi=tmp,rt=u;}ll f[N*2][2],g[N*2][2],t[N*2],ans;void cal(int u,int fa,int dis,int deep){depth=max(depth,deep);if (t[dis+n]) g[n+dis][1]++;else g[n+dis][0]++;t[dis+n]++;for (int i=head[u];i;i=edge[i].pre)if (edge[i].to!=fa&&!vis[edge[i].to])cal(edge[i].to,u,dis+edge[i].dis,deep+1);t[dis+n]--;}void work(int u){f[n][0]=1;int mx=0;for (int i=head[u];i;i=edge[i].pre)if (!vis[edge[i].to]){depth=0;cal(edge[i].to,u,edge[i].dis,1);mx=max(mx,depth);ans+=(f[n][0]-1)*g[n][0];for (int j=n-depth;j<=n+depth;j++) ans+=f[j][0]*g[n*2-j][1]+f[j][1]*g[n*2-j][0]+f[j][1]*g[n*2-j][1];for (int j=n-depth;j<=n+depth;j++){f[j][0]+=g[j][0];f[j][1]+=g[j][1];g[j][0]=g[j][1]=0;}}for (int i=n-mx;i<=n+mx;i++) f[i][0]=f[i][1]=0;}void dfs(int u){getsize(u,0);mi=inf;sum=size[u];getrt(u,0);u=rt;vis[u]=true;work(u);for (int i=head[u];i;i=edge[i].pre)if (!vis[edge[i].to]) dfs(edge[i].to);}int main(){scanf("%d",&n);int x,z,y;for (int i=1;i<n;i++){scanf("%d%d%d",&x,&y,&z);addedge(x,y,z);addedge(y,x,z);}dfs(1);printf("%lld",ans);return 0;}
总结
1:这道题的复杂度并不是那么明确,但是也算是一种方法。
2:前缀路径和,求差如果是0,那么就代表这段区间之间的权值和为0,也就是出现休息站了。
3:mx和depth,控制最大的范围,在这个范围之内计算和更新。并且在点分治中,只clear()刚刚访问过的点(或是在范围内的),避免memset等出现,防止没有必要的计算出现还是很重要的。
4:好题,须仔细回顾
- bzoj3697(点分治)
- BZOJ3697【点分治】
- BZOJ3697 点分治
- [BZOJ3697]采药人的路径(点分治)
- [BZOJ3697]采药人的路径(点分治)
- bzoj3697 采药人的路径 (点分治)
- [BZOJ3697]采药人的路径(点分治)
- 【BZOJ3697】采药人的路径【点分治】
- 【bzoj3697】【采药人的路径】【点分治】
- 【bzoj3697】采药人的路径 点分治
- bzoj3697 采药人的路径 点分治
- 【bzoj3697】【坑】采药人的路径 点分治
- Bzoj3697:采药人的路径:树的点分治
- [BZOJ3697][[FJ2014集训]采药人的路径][点分治]
- 树分治(点分治+边分治)
- BZOJ3697
- 【Luogu3806】点分治(点分治)
- poj1741(点分治)
- 20162017-ct-s03e02-codeforces-trainings-season-3-episode-2-en-I Painting the natural numbers
- 编写Js SDK获取网页信息(包含10多项内容)
- Elasticsearch2.x Doc values
- 优酷上不错的最优化教学视频地址
- Cocos2dx 菜单项控件-Menu
- bzoj3697(点分治)
- 深度学习的学习流程之基础
- shell程序设计的流程控制及其命令分组
- HashMap的工作原理
- JVM监测指令
- ExtJs 入门笔记1[Hello World]
- Java中强引用、软引用、弱引用、虚引用内容解析------夜空中最亮的星
- jsp源码审计的积累
- java语言有哪些优点?