五十分很送,七十分也水。
斐波那契数列有两个性质:
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;
}
只是对拍了一下,正确性不能保证,但应该没问题。