NOI前总结:点分治
2015-07-06 20:48
330 查看
点分治:
点分治的题目基本一样,都是路径计数。
其复杂度的保证是依靠 $O(n)$ 找重心的,每一次至少将问题规模减小为原先的$1/2$。
找重心我喜欢$BFS$防止爆栈。
故总共有 $O(logn)$ 层。
在每一层我们分别对不同的块(删点而形成)采用 $O(siz[p])$ 的算法。
主定理 $T(n) = T(n/2) + O(n)$
总体上是 $O(nlogn)$
大体框架如下
然后对于点分治路径的统计,通常有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$
View Code
总结完了点分治,NOI必胜。
点分治的题目基本一样,都是路径计数。
其复杂度的保证是依靠 $O(n)$ 找重心的,每一次至少将问题规模减小为原先的$1/2$。
找重心我喜欢$BFS$防止爆栈。
int Root(int x){ dfsn[0]=0; q.push(x); fa[x]=0; flag[x]=1; while(!q.empty()){ int x=q.front(); q.pop(); dfsn[++dfsn[0]]=x; for(int i=g[x];i;i=E[i].to) if(!v[p] && !flag[p]){ fa[p]=x; flag[p]=1; q.push(p); } } for(int i=1;i<=dfsn[0];i++){ siz[dfsn[i]]=1; h[dfsn[i]]=0; flag[dfsn[i]]=0; } int root=0; for(int i=dfsn[0];i>=1;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[0]-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]=1; 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 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 ,f[M],siz ,h ,fa ,dfsn ,S ,d ; int C[M][M]; bool v ,flag ; 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<<1]; void ade(int x,int y){ E[++totE]=(edge){y,g[x]}; g[x]=totE; } int qpow(int x,int n){ int ans=1; for(;n;n>>=1,x=mul(x,x)) if(n&1) ans=mul(ans,x); return ans; } int Root(int x){ dfsn[0]=0; q.push(x); fa[x]=0; flag[x]=1; while(!q.empty()){ int x=q.front(); q.pop(); dfsn[++dfsn[0]]=x; for(int i=g[x];i;i=E[i].to) if(!v[p] && !flag[p]){ fa[p]=x; flag[p]=1; q.push(p); } } for(int i=1;i<=dfsn[0];i++){ siz[dfsn[i]]=1; h[dfsn[i]]=0; flag[dfsn[i]]=0; } int root=0; for(int i=dfsn[0];i>=1;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[0]-siz[x]); if(!root || h[x]<h[root]) root=x; } return root; } void bfs(int x){ dfsn[0]=0; d[x]=1; q.push(x); flag[x]=1; while(!q.empty()){ int x=q.front(); q.pop(); dfsn[++dfsn[0]]=x; for(int i=g[x];i;i=E[i].to) if(!v[p] && !flag[p]){ d[p]=d[x]+1; flag[p]=1; q.push(p); } } for(int i=1;i<=dfsn[0];i++){ int tmp=1; for(int j=0;j<=K;j++){ f[j]=add(f[j],tmp); tmp=mul(tmp,d[dfsn[i]]); } flag[dfsn[i]]=0; } } int power[M]; void solve(int rt){ dfsn[0]=0; d[rt]=1; q.push(rt); flag[rt]=1; while(!q.empty()){ int x=q.front(); q.pop(); dfsn[++dfsn[0]]=x; for(int i=g[x];i;i=E[i].to) if(!v[p] && !flag[p]){ d[p]=d[x]+1; flag[p]=1; q.push(p); } } for(int i=1;i<=dfsn[0];i++){ int tmp=1; for(int j=0;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=1;t<=dfsn[0];t++){ int x=dfsn[t]; flag[x]=0; power[0]=1; for(int i=1;i<=K;i++) power[i]=mul(power[i-1],d[x]); for(int i=0;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=1;i<=dfsn[0];i++){ int tmp=1; for(int j=0;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]=1; // 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=0;i<=K;i++) f[i]=0; 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[0][0]=1; for(int i=1;i<=K;i++){ C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=add(C[i-1][j-1],C[i-1][j]); } int L,now,A,B,Q,tmp; scanf("%d%d%d%d%d",&L,&now,&A,&B,&Q); for(int i=1,x,y;i<n;i++){ now=(now*A+B)%Q; tmp=(i<L)? i:L; x=i-now%tmp; y=i+1; ade(x,y); ade(y,x); } DC(Root(1)); for(int i=1;i<=n;i++){ printf("%d\n",S[i]); } return 0; }
View Code
总结完了点分治,NOI必胜。
相关文章推荐
- 守护线程和用户线程
- Going Home - POJ 1295 KM算法
- 排序之归并排序
- 网页开发之字体(一)
- Deep Learning 的一些认识
- 115太酷了,居然出了个TV版客户端
- isKindOfClass和isMemberOfClass的区别
- Beauty World OpenCart 主题模板 ABC-0046
- 在上一篇的基础上的追加——关于使用plsql developer的数据库表的导入和导
- 【c语言】第一个只出现一次的字符题目:在字符串中找出第一个只出现一次的字符
- Python学习笔记_Python对象
- 提升树算法 详解
- 11gR203 inventory 被误删后的恢复
- Beauty World OpenCart 主题模板 ABC-0046
- 数据归一化和两种常用的归一化方法
- C++该typeid和dynamic_cast
- Java集合之ArrayList源码分析
- The connection to adb is down, and a severe error has occured.
- Centos YUM国内163源
- Davinci内核镜像uImage的编译