您的位置:首页 > 其它

NOI前总结:点分治

2015-07-06 20:48 330 查看
点分治:

点分治的题目基本一样,都是路径计数。

其复杂度的保证是依靠 $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必胜。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: