NOI前总结:点分治
点分治:
点分治的题目基本一样,都是路径计数。
其复杂度的保证是依靠 $O(n)$ 找重心的,每一次至少将问题规模减小为原先的$1/2$。
找重心我喜欢$BFS$防止爆栈。
- int Root(int x){
- dfsn[]=;
- q.push(x); fa[x]=;
- flag[x]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- fa[p]=x;
- flag[p]=;
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++){
- siz[dfsn[i]]=;
- h[dfsn[i]]=;
- flag[dfsn[i]]=;
- }
- int root=;
- for(int i=dfsn[];i>=;i--){
- int x=dfsn[i];
- if(fa[x]){
- siz[fa[x]]+=siz[x];
- h[fa[x]]=max(h[fa[x]],siz[x]);
- }
- h[x]=max(h[x],dfsn[]-siz[x]);
- if(!root || h[x]<h[root]) root=x;
- }
- return root;
- }
故总共有 $O(logn)$ 层。
在每一层我们分别对不同的块(删点而形成)采用 $O(siz[p])$ 的算法。
主定理 $T(n) = T(n/2) + O(n)$
总体上是 $O(nlogn)$
大体框架如下
- void DC(int x){
- v[x]=;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]){
- // 大体上是f[x]*ft[x]就是
- // Ans = (之前的子树的路径数)*(当前子树的路径数)
- }
- // 将标记什么的清空,注意保证复杂度是O(siz)不是O(n)
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]) DC(Root(p));
- }
然后对于点分治路径的统计,通常有dp,数据结构,数论等等的方法。
注意:要记得上面的方法没有统计以点x为起点的路径条数,记得加上。
例题:
BZOJ 3697
题意:
给出一棵树,每一条边为黑或白,统计满足条件的路径数
1.路径上黑色和白色的个数相等
2.路径上存在一个点使得起点到当前点黑色和白色的个数相等,此点不能是起点终点。
乍一看是没有解法的,套用点分治。
问题转化为统计过点x的合法路径条数。
套用dp
$f(x,0)$ 表示和为x,无休息站的
$f(x,1)$ 表示和为x,有休息站的
$$ans = f(0,0) * ft(0,0) + \sum_{i=-d}^d {f(i,0) \cdot ft(-i,1) + f(i,1) \cdot ft(-i,0) + f(i,1) \cdot ft(-i,1)} $$
条件2可以转化为在当前点到x的路径上有点的$dis(p) = dis(now)$
所以注意保证初始化的复杂度
所以记录一下当前的最大深度,初始化 $f$ 数组和 $ft$ 数组的时候从0循环到 $max deep$
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <queue>
- #define N 400010
- #define p E[i].x
- #define LL long long
- #define debug(x) cout<<#x<<" = "<<x<<endl;
- /*
- 树形dp
- f(x,0) 表示和为x,无休息站的
- f(x,1) 表示和为x,有休息站的
- ans = f[0][0] * ft[0][0] +
- ∑ f[i][0]*ft[-i][1] + f[i][1]*ft[-i][0] + f[i][1]*ft[-i][1]
- (-d <= i <= d)
- */
- using namespace std;
- struct edge{
- int x,to,v;
- }E[N<<];
- int n,totE;
- int g[N],dfsn[N],fa[N],siz[N],h[N];
- bool v[N],flag[N];
- LL ans;
- LL f[N][],ft[N][];
- queue<int> q;
- void ade(int x,int y,int v){
- E[++totE]=(edge){y,g[x],v}; g[x]=totE;
- }
- int Root(int x){
- dfsn[]=;
- q.push(x); fa[x]=;
- flag[x]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- fa[p]=x;
- flag[p]=;
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++){
- siz[dfsn[i]]=;
- h[dfsn[i]]=;
- flag[dfsn[i]]=;
- }
- int root=;
- for(int i=dfsn[];i>=;i--){
- int x=dfsn[i];
- if(fa[x]){
- siz[fa[x]]+=siz[x];
- h[fa[x]]=max(h[fa[x]],siz[x]);
- }
- h[x]=max(h[x],dfsn[]-siz[x]);
- if(!root || h[x]<h[root]) root=x;
- }
- return root;
- }
- int mxdep;
- int cnt[N],d[N],dis[N];
- void dfs(int x,int fa){
- mxdep=max(mxdep,d[x]);
- if(cnt[dis[x]]) ft[dis[x]][]++;
- else ft[dis[x]][]++;
- cnt[dis[x]]++;
- for(int i=g[x];i;i=E[i].to)
- if(p!=fa&&!v[p]){
- d[p]=d[x]+;
- dis[p]=dis[x]+E[i].v;
- dfs(p,x);
- }
- cnt[dis[x]]--;
- }
- void DC(int x){
- v[x]=;
- f[n][]=;
- int mx=;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]){
- dis[p]=n+E[i].v;
- d[p]=mxdep=;
- dfs(p,p);
- mx=max(mx,mxdep);
- ans+=(f[n][]-)*ft[n][];
- for(int j=-mxdep;j<=mxdep;j++){
- ans+=ft[n-j][]*f[n+j][]+
- ft[n-j][]*f[n+j][]+ft[n-j][]*f[n+j][];
- }
- for(int j=n-mxdep;j<=n+mxdep;j++){
- f[j][]+=ft[j][];
- f[j][]+=ft[j][];
- ft[j][]=ft[j][]=;
- }
- }
- for(int i=n-mx;i<=n+mx;i++)
- f[i][]=f[i][]=;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]) DC(Root(p));
- }
- int main(){
- scanf("%d",&n);
- for(int i=,x,y;i<n;i++){
- int v;
- scanf("%d%d%d",&x,&y,&v);
- if(!v) v=-;
- ade(x,y,v); ade(y,x,v);
- }
- DC(Root());
- printf("%lld\n",ans);
- return ;
- }
然后是HDU 4812
题意:给出一棵树,每一个点有一个点权,找到一条点权乘积为K的路径,输出起点和终点,要求字典序最小。
求一下逆元,然后用map记录前面的所有子树能够到达的权值(乘积)
注意这是点权,不同于边权,所以在处理过点x的路径的时候要谨慎,我的做法是将路径拆为 x点 , 之前子树中的一条链, 当前子树中的一条链。
用map复杂度多一个log,然而也能过。
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <map>
- #include <queue>
- #define N 400010
- #define LL long long
- #define mod 1000003
- #define p E[i].x
- #define INF 0x3f3f3f3f
- #define ipos map<int,int>::iterator
- using namespace std;
- struct edge{
- int x,to;
- }E[N<<];
- map<int,int> ft,f;
- int n,ansv[],totE,K;
- int h[N],g[N],siz[N],a[N],fa[N],dfsn[N],dis[N];
- bool v[N],flag[N];
- queue<int> q;
- int add(int a,int b){
- if(a+b>=mod) return a+b-mod;
- return a+b;
- }
- int mul(int a,int b){
- return (int)((LL)a*(LL)b%mod);
- }
- int qpow(int x,int n){
- int ans=;
- for(;n;n>>=,x=mul(x,x))
- if(n&) ans=mul(ans,x);
- return ans;
- }
- int inv(int x){
- return (int)qpow((LL)x,mod-2LL);
- }
- void ade(int x,int y){
- E[++totE]=(edge){y,g[x]}; g[x]=totE;
- }
- int Root(int x){
- dfsn[]=;
- q.push(x); fa[x]=;
- flag[x]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- fa[p]=x;
- flag[p]=;
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++){
- siz[dfsn[i]]=;
- h[dfsn[i]]=;
- flag[dfsn[i]]=;
- }
- int root=;
- for(int i=dfsn[];i>=;i--){
- int x=dfsn[i];
- if(fa[x]){
- siz[fa[x]]+=siz[x];
- h[fa[x]]=max(h[fa[x]],siz[x]);
- }
- h[x]=max(h[x],dfsn[]-siz[x]);
- if(!root || h[x]<h[root]) root=x;
- }
- return root;
- }
- void bfs(int x){
- dfsn[]=;
- q.push(x); flag[x]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- if(!ft.count(dis[x]) || ft[dis[x]]>x)
- ft[dis[x]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- flag[p]=;
- dis[p]=mul(dis[x],a[p]);
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++)
- flag[dfsn[i]]=;
- }
- void upd(int a,int b){
- if(a>b) swap(a,b);
- if(ansv[]>a || ansv[]==a&&ansv[]>b){
- ansv[]=a;
- ansv[]=b;
- }
- }
- void DC(int x){
- // printf("node : %d\n",x);
- v[x]=;
- f.clear();
- f[]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]){
- ft.clear();
- dis[p]=a[p];
- bfs(p);
- for(ipos it=ft.begin();it!=ft.end();it++){
- int tmp=(*it).first;
- if(f.count(mul(mul(K,inv(tmp)),inv(a[x])))){
- upd(f[mul(mul(K,inv(tmp)),inv(a[x]))],
- (*it).second);
- }
- }
- for(ipos it=ft.begin();it!=ft.end();it++){
- if(!f.count((*it).first)) f[(*it).first]=(*it).second;
- else f[(*it).first]=min(f[(*it).first],(*it).second);
- }
- }
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]) DC(Root(p));
- }
- int main(){
- // freopen("test.in","r",stdin);
- while(scanf("%d%d",&n,&K)==){
- ansv[]=ansv[]=INF;
- totE=;
- for(int i=;i<=n;i++) g[i]=;
- for(int i=;i<=n;i++) scanf("%d",&a[i]),v[i]=;
- for(int i=,x,y;i<n;i++){
- int v;
- scanf("%d%d",&x,&y);
- ade(x,y);
- ade(y,x);
- }
- DC(Root());
- if(ansv[]==INF) puts("No solution");
- else printf("%d %d\n",ansv[],ansv[]);
- }
- return ;
- }
最后是 国家集训队的 crash的文明世界
题意:
定义:
求所有点的S(i)
很显然是点分治,斯特林数是什么,我不会 TAT
记录$f(i)$表示$\sum {dist(p,x)^i }$,$ft$同理
考虑每一次统计过x的路径时将过x的路径的影响加入子树中的点,同时在最后将$S(i)$加上$f(K)$
坑点就是合并的时候要用一下 二项式展开
$$ S(p) += \sum_{i=0}^K {C(K,i) * f(K-i) * b^i}$$
然后注意要统计全路径,所以上面的框架不适用(上面的框架对于当前子树只能统计遍历过的子树,而应该是统计所有除了当前子树的权值和)
然后就可以了,自己歪歪(看不懂题解),一遍AC爽。
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <queue>
- #define N 200010
- #define mod 10007
- #define M 310
- #define p E[i].x
- using namespace std;
- /*
- f[j] 之前的 dist(x,p)^j
- ft[j] 当前的 dist(x,p)^j
- S[p] += ∑C(K,i) * a^{K-i} * b^i (0<=i<=K)
- O(lognK)
- */
- int n,K,totE;
- int g[N],f[M],siz[N],h[N],fa[N],dfsn[N],S[N],d[N];
- int C[M][M];
- bool v[N],flag[N];
- queue<int> q;
- int add(int a,int b){
- if(a+b>=mod) return a+b-mod;
- return a+b;
- }
- int mul(int a,int b){
- return a*b%mod;
- }
- struct edge{
- int x,to;
- }E[N<<];
- void ade(int x,int y){
- E[++totE]=(edge){y,g[x]}; g[x]=totE;
- }
- int qpow(int x,int n){
- int ans=;
- for(;n;n>>=,x=mul(x,x))
- if(n&) ans=mul(ans,x);
- return ans;
- }
- int Root(int x){
- dfsn[]=;
- q.push(x); fa[x]=;
- flag[x]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- fa[p]=x;
- flag[p]=;
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++){
- siz[dfsn[i]]=;
- h[dfsn[i]]=;
- flag[dfsn[i]]=;
- }
- int root=;
- for(int i=dfsn[];i>=;i--){
- int x=dfsn[i];
- if(fa[x]){
- siz[fa[x]]+=siz[x];
- h[fa[x]]=max(h[fa[x]],siz[x]);
- }
- h[x]=max(h[x],dfsn[]-siz[x]);
- if(!root || h[x]<h[root]) root=x;
- }
- return root;
- }
- void bfs(int x){
- dfsn[]=; d[x]=;
- q.push(x); flag[x]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- d[p]=d[x]+;
- flag[p]=;
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++){
- int tmp=;
- for(int j=;j<=K;j++){
- f[j]=add(f[j],tmp);
- tmp=mul(tmp,d[dfsn[i]]);
- }
- flag[dfsn[i]]=;
- }
- }
- int power[M];
- void solve(int rt){
- dfsn[]=; d[rt]=;
- q.push(rt); flag[rt]=;
- while(!q.empty()){
- int x=q.front(); q.pop();
- dfsn[++dfsn[]]=x;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p] && !flag[p]){
- d[p]=d[x]+;
- flag[p]=;
- q.push(p);
- }
- }
- for(int i=;i<=dfsn[];i++){
- int tmp=;
- for(int j=;j<=K;j++){
- f[j]=(f[j]-tmp+mod)%mod;
- tmp=mul(tmp,d[dfsn[i]]);
- }
- }
- // printf("son : %d\n",rt);
- // for(int i=0;i<=K;i++){
- // printf("%d%c",f[i],i==K?'\n':' ');
- // }
- for(int t=;t<=dfsn[];t++){
- int x=dfsn[t];
- flag[x]=;
- power[]=;
- for(int i=;i<=K;i++) power[i]=mul(power[i-],d[x]);
- for(int i=;i<=K;i++){
- // printf("addto %d = %d\n",x,mul(C[K][i], mul(f[K-i],power[i])));
- S[x] = add(S[x], mul(C[K][i], mul(f[K-i],power[i])));
- }
- S[x]=add(S[x],power[K]);
- }
- // S[x]=add(S[x],power[K]);
- for(int i=;i<=dfsn[];i++){
- int tmp=;
- for(int j=;j<=K;j++){
- f[j]=add(f[j],tmp);
- tmp=mul(tmp,d[dfsn[i]]);
- }
- }
- }
- //S[p] += ∑C(K,i) * a^{K-i} * b^i (0<=i<=K)
- void DC(int x){
- v[x]=;
- // printf("node : %d\n",x);
- // for(int i=1;i<=dfsn[0];i++)
- // printf("%d%c",dfsn[i],i==dfsn[0]?'\n':' ');
- for(int i=;i<=K;i++) f[i]=;
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]) bfs(p);
- // printf("before\n");
- // for(int i=0;i<=K;i++) printf("%d%c",f[i],i==K?'\n':' ');
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]) solve(p);
- S[x]=add(S[x],f[K]);
- // printf("base = %d\n",f[K]);
- for(int i=g[x];i;i=E[i].to)
- if(!v[p]) DC(Root(p));
- }
- int main(){
- freopen("civilization.in","r",stdin);
- freopen("civilization.out","w",stdout);
- scanf("%d%d",&n,&K);
- C[][]=;
- for(int i=;i<=K;i++){
- C[i][]=;
- for(int j=;j<=i;j++)
- C[i][j]=add(C[i-][j-],C[i-][j]);
- }
- int L,now,A,B,Q,tmp;
- scanf("%d%d%d%d%d",&L,&now,&A,&B,&Q);
- for(int i=,x,y;i<n;i++){
- now=(now*A+B)%Q;
- tmp=(i<L)? i:L;
- x=i-now%tmp;
- y=i+;
- ade(x,y);
- ade(y,x);
- }
- DC(Root());
- for(int i=;i<=n;i++){
- printf("%d\n",S[i]);
- }
- return ;
- }
总结完了点分治,NOI必胜。
NOI前总结:点分治的更多相关文章
- NOI前各种Idea总结以及各种文本乱堆
转载请注明原文地址:https://www.cnblogs.com/LadyLex/p/9227267.html 不过这篇的确没什么*用了转转吧 2018-6-24 关于一类延迟标记(来自UR14 思 ...
- NOI前训练日记
向别人学习一波,记点流水帐.17.5.29开坑. 5.29 早晨看了道据说是树状数组优化DP的题(hdu5542),然后脑补了一个复杂度500^3的meet in the middle.然后死T... ...
- [日常] NOI前划水日记
NOI前划水日记 开坑记录一下每天的效率有多低 5.24 早上被春哥安排了一场NEERC(不过怎么是qualification round啊) 省队势力都跑去参加THU/PKU夏令营了...剩下四个D ...
- NOI前的考试日志
4.14 网络流专项测试 先看T1,不会,看T2,仙人掌???wtf??弃疗.看T3,貌似最可做了,然后开始刚,刚了30min无果,打了50分暴力,然后接着去看T1,把序列差分了一下,推了会式子,发现 ...
- NOI.AC 32 Sort——分治
题目:http://noi.ac/problem/32 从全是0和1的情况入手,可以像线段树一样分治下去,回到本层的时候就是左半部的右边是1,右半部的左边是0,把这两部分换一下就行.代价和时间一样是n ...
- noi前机房日常
2015/6/16 上午a了一道省选分组赛day1t2,并在cf100553H双倍经验,好评 bzoj3152(ctsc2013)贪心,用priority_queue要清空 bx2k上午交了几十题,他 ...
- NOI前总结
最近也就是天天考试,总结一下. 7.1 开场T1T2都是不可做的概率期望,只有T3看起来可做,于是怒干4h+,将题解里面的所有结论都推出来了,大模拟写的一点毛病都没有,可还是因为2-SAT掌握不熟结果 ...
- 博客索引and题目列表
目录 笔记整理 计划 要学的东西 缺省源 要做的题 搜索 高斯消元 矩阵 排列组合 2019.7.9 2019.7.10 kmp ac自动机 2019.7.11 2019.7.15 笔记整理 1.同余 ...
- NOI冲刺计划
省选过了,剩下大概是NOI冲刺了吧.中间还有一大堆诸如会考,CTSC,APIO等东西. 最近先不急着天天刷八中了吧,多在不同网站见一些题,然后再着重提高一下代码准确性.重点把DP这个板块多练习一下,八 ...
随机推荐
- 关于克隆gitlab项目的一些 问题列表
1. gitLab项目clone过后, 运行ng serve出错: 解决方式: npm install -g node-gyp npm install --global --production wi ...
- 【面试 struts2】【第三篇】struts2的问题
1.struts2的工作流程 1>客户端浏览器发出HTTP请求. 2>根据web.xml配置,该请求被FilterDispatcher接收 3>根据struts.xml配置,找到需要 ...
- Node.js机制及原理理解初步
http://blog.csdn.net/leftfist/article/details/41891407 一.node.js优缺点 node.js是单线程. 好处就是 1)简单 2)高性能,避免了 ...
- 《Getting Started with WebRTC》第二章 WebRTC技术介绍
<Getting Started with WebRTC>第二章 WebRTC技术介绍 本章作WebRTC的技术介绍,主要讲下面的概念: . 怎样建立P2P的通信 . 有效的信 ...
- C#压缩或解压(rar和zip文件)
/// <summary> /// 解压RAR和ZIP文件(需存在Winrar.exe(只要自己电脑上可以解压或压缩文件就存在Winrar.exe)) /// </summary&g ...
- maven插件介绍之maven-jar-plugin
maven-jar-plugin 插件的maven依赖为: <dependency> <groupId>org.apache.maven.plugins</groupId ...
- vue 开发前准备工作
工欲善其事,必先利其器. 第一步,选择一个合适的编辑器: 看vue官网上,有推荐用HBuilder X这个编辑器,这个我在开发微信小程序的时候,就是用的这个编辑器,还可以,挺好用的,也可以用git做版 ...
- JavaScript随机数区间限制
在一段区间内的取某个数字 有一个通用的方法 主要使用了一下两个javascript函数 1.Math.floor() 方法可对一个数进行下舍入 2.Math.random() 方法可返回介于 0 ~ ...
- spark0.9.1集群模式执行graphx測试程序(LiveJournalPageRank,新增Connected Components)
spark最新版公布了.之前的版本号就已经集成了graphx,这个版本号还改了一些bug. 我做了简单測试,只是网上关于集群模式执行spark资料太少了,仅仅有关于EC2(见參考资料1)的.可是还非常 ...
- [转]XCode中修改缺省公司名称/开发人员名称
本文转载至 http://www.cnblogs.com/zhulin/archive/2011/11/24/2261537.html XCode新建文件后,头部会有开发人员名称,公司名称等信息 ...