斐波那契樹(動態點分治)

本題謝絕轉載
這裏寫圖片描述五十分很送,七十分也水。

斐波那契數列有兩個性質:
1.兩個斐波那契數列相加,還是斐波那契數列。
2.斐波那契數列斷開,還是斐波那契數列。

一個點的權值,只與首項的值(也就是起點的a和b,但是這個點的權值只表現出a)以及首項到它的距離有關。
對於70分,首項位置確定,它到所有點的距離可以預處理。
只需要維護首項的值,建立兩個樹狀數組,下標是到起點的距離+1(因爲update(0,val)以及其他一些東西會出現問題,所以下標統統+1),值是a和b。修改時在1加a加b,在m+2減a減b。詢問時getsum(dist+1)即可得到首項。

對於100分,用出題者的話說,“不停地換點做修改,很明顯是點分治啦”。
於是,動態點分治。
每一次在點分樹往上爬,相當於把斐波那契數列截斷,算出新的首項和距離在該點更新。爲了避免重複計算,需要記錄是從哪個方向爬過來的,新開樹狀數組計算重複,詢問時減掉即可。
詢問就是在該點往上爬,算出它自己以及點分樹上它的父親對它的影響。

後話:
自學了點分治,前前後後花了一個多星期的零散時間寫這道題。
修了各種bug,研究vector數組套結構體vector。
順便試了一下類似於RMQ求LCA的東西。原理:https://www.cnblogs.com/scau20110726/archive/2013/05/26/3100812.html
打出了有史以來最長的代碼,下面附的還是精簡版。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int Mod=1e9+7;
struct BIT {
	int size;
	vector<LL> c1,c2;
	inline void init(int dist) {
		size=dist+1;
		c1.resize(size+1);
		c2.resize(size+1);
	}
	inline int lowbit(int x) {
		return x&-x;
	}
	inline void update(int x,LL val1,LL val2) {
		if (val1<0) val1+=Mod;
		if (val2<0) val2+=Mod;
		for (;x<=size;x+=lowbit(x)) {
			c1[x]+=val1;c2[x]+=val2;
			if (c1[x]>=Mod) c1[x]-=Mod;
			if (c2[x]>=Mod) c2[x]-=Mod;
		}
	}
	inline void getsum(int x,LL &a,LL &b) {
		a=b=0;
		for (;x;x-=lowbit(x)) {
			a+=c1[x];b+=c2[x];
			if (a>=Mod) a-=Mod;
			if (b>=Mod) b-=Mod;
		}
	}
};
vector<BIT> node[200005];
int head[200005],vet[400005],nxt[400005],num;
int Dep[200005],R[400005],F[200005],st[400005][19],ti,pow2[19];
int sz[200005],mxs[200005],root,mxson;
int fa[200005],idx[200005];
LL fib1[200005],fib2[200005];
bool vis[200005];
int n,q;
void addedge(int x,int y) {
	num++;vet[num]=y;nxt[num]=head[x];head[x]=num;
	num++;vet[num]=x;nxt[num]=head[y];head[y]=num;
}
inline int read() {
	char ch=0;int sum=0;
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch<='9' && ch>='0') sum=sum*10+ch-'0',ch=getchar();
	return sum;
}
void dfs(int x,int dep,int ff) {
	Dep[x]=dep;
	R[++ti]=dep;
	F[x]=ti;
	for (int e=head[x];e;e=nxt[e]) {
		if (vet[e]==ff) continue;
		int v=vet[e];
		dfs(v,dep+1,x);
		R[++ti]=dep;
	}
}
void ST(int len) {
	int K=(int)(log((double)len)/log(2.0));
	for (int i=1;i<=len;i++) st[i][0]=R[i];
	for (int i=1;i<=K;i++) {
		for (int j=1;j+pow2[i]-1<=len;j++) {
			st[j][i]=min(st[j][i-1],st[j+pow2[i-1]][i-1]);
		}
	}
}
int getdist(int u,int v) {
	int x=F[u],y=F[v];
    if (x>y) swap(x,y);
	int K=(int)(log((double)y-x+1)/log(2.0));
	return Dep[u]+Dep[v]-2*min(st[x][K],st[y-pow2[K]+1][K]);
}

void getroot(int x,int ff,int tt) {
    sz[x]=1;mxs[x]=0;
    for (int e=head[x];e;e=nxt[e]) {
        int v=vet[e];
        if (v==ff || vis[v]) continue;
        getroot(v,x,tt);
        sz[x]+=sz[v];
        mxs[x]=max(mxs[x],sz[v]);
    }
    mxs[x]=max(mxs[x],tt-sz[x]);
    if (mxs[x]<mxs[root]) root=x,mxson=mxs[x];
}
void solve(int x,int tot) {
    vis[x]=true;int son=0,mx=mxson;
    for (int e=head[x];e;e=nxt[e]) {
        if (!vis[vet[e]]) {
            root=0;
            int tt=sz[vet[e]];
            if (tt>sz[x]) tt=tot-sz[x];
            getroot(vet[e],x,tt);
            fa[root]=x;
            idx[root]=++son;
            solve(root,tt);
        }
    }
    node[x].resize(son+1);
    if (!son) {
    	node[x][0].init(0);
    	return;
	}
	for (int i=0;i<=son;i++) node[x][i].init(mx);
}
inline LL getfib(LL a,LL b,int dist) {
	return (fib1[dist]*a+fib2[dist]*b)%Mod;
}
inline void modify(int x,int m,LL a,LL b) {
	int u=x,ff=fa[u];
	node[u][0].update(1,a,b);
	if (node[u][0].size>=m+2) {
		node[u][0].update(m+2,-a,-b);
	}
	while (ff) {
		int dist=getdist(x,ff);
		if (dist<=m) {
			LL tmp1=getfib(a,b,dist),tmp2=getfib(a,b,dist+1);
			dist=m-dist;
			node[ff][0].update(1,tmp1,tmp2);
			node[ff][idx[u]].update(1,tmp1,tmp2);
			if (dist+2<=node[fa[u]][0].size) {
				node[ff][0].update(dist+2,-tmp1,-tmp2);
				node[ff][idx[u]].update(dist+2,-tmp1,-tmp2);
			}
		}
		u=ff;
		ff=fa[u];
	}
}
inline LL query(int x) {
	int u=x,ff=fa[u];
	LL ans=node[u][0].c1[1],a=0,b=0;
	while (ff) {
		int dist=getdist(x,ff);
		if (dist<node[ff][0].size) {
			node[ff][0].getsum(dist+1,a,b);
			ans+=getfib(a,b,dist);
			if (ans>=Mod) ans-=Mod;
			node[ff][idx[u]].getsum(dist+1,a,b);
			ans-=getfib(a,b,dist);
			if (ans<0) ans+=Mod;
		}
		else cout<<"ERROR\n";
		u=ff;
		ff=fa[u];
	}
	return ans;
}
int main() {
	freopen("fibtree.in","r",stdin);
	freopen("fibtree.out","w",stdout);
	n=read();q=read();
	for (int i=1;i<n;i++) {
		int x=read(),y=read();
		addedge(x,y);
	}
	
	pow2[0]=1;
	for (int i=1;i<=18;i++) pow2[i]=pow2[i-1]<<1;
	dfs(1,1,0);
	ST(n*2-1);
	fib1[0]=1;fib2[1]=1;
	for (int i=2;i<=n;i++) {
		fib1[i]=fib1[i-1]+fib1[i-2];
		if (fib1[i]>=Mod) fib1[i]-=Mod;
		fib2[i]=fib2[i-1]+fib2[i-2];
		if (fib2[i]>=Mod) fib2[i]-=Mod;
	}
	
	mxs[0]=(n<<1);root=0;
    getroot(1,0,n);
    solve(root,n);
	
	for (int i=1;i<=q;i++) {
		int op=read(),u=read();
		if (op==1) {
			int m=read(),a=read(),b=read();
			modify(u,m,a,b);
		}
		else {
			printf("%lld\n",query(u));
		}
	}
	return 0;
}

只是對拍了一下,正確性不能保證,但應該沒問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章