0%

原理:https://www.cnblogs.com/Paul-Guderian/p/6933799.html

1.普通莫队
https://www.luogu.org/problemnew/show/P1494
对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
struct node {
int l,r,ID;
} qu[50005];
int be[50005],color[50005],sum[50005];
LL ans1[50005],ans2[50005],ans;
int l,r;
int n,m,bs;
bool cmp(node a,node b) {
if (be[a.l]!=be[b.l]) return a.l<b.l;
return a.r<b.r;
}
inline void update(int col,int val) {
if (val==-1) ans-=2*sum[col]-2,sum[col]--;
else ans+=2*sum[col],sum[col]++;
}
LL gcd(LL a,LL b) {
if (b==0) return a;
return gcd(b,a%b);
}
int main() {
scanf("%d%d",&n,&m);bs=sqrt(n);
for (int i=1;i<=n;i++) {scanf("%d",&color[i]);be[i]=(i-1)/bs+1;}
for (int i=1;i<=m;i++) {scanf("%d%d",&qu[i].l,&qu[i].r);qu[i].ID=i;}
sort(qu+1,qu+m+1,cmp);
l=1,r=0;//先把区间记空
for (int i=1;i<=m;i++) {
while (l>qu[i].l) update(color[l-1],1),l--;
while (r<qu[i].r) update(color[r+1],1),r++;
while (l<qu[i].l) update(color[l],-1),l++;
while (r>qu[i].r) update(color[r],-1),r--;
int ID=qu[i].ID;
ans1[ID]=ans;ans2[ID]=(LL)(qu[i].r-qu[i].l+1)*(qu[i].r-qu[i].l);
if (ans==0) ans2[ID]=1;
LL tmp=gcd(ans1[ID],ans2[ID]);
ans1[ID]/=tmp;ans2[ID]/=tmp;
}
for (int i=1;i<=m;i++) printf("%lld/%lld\n",ans1[i],ans2[i]);
return 0;
}
/*
-1:(sum-1)*(sum-2)-(sum)*(sum-1)=-2*sum+2
+1:(sum+1)*(sum)-(sum)*(sum-1)=2*sum
*/

2.带修莫队
https://www.luogu.org/problemnew/show/P1903
Q L R 代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。
R P Col 把第P支画笔替换为颜色Col。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
struct node1 {
int l,r,ti,ID;
} query[50005];
struct node2 {
int x,newc,oldc;
} change[50005];
int be[50005],color[50005],cc[50005],cnt[1000005];
int Ans[50005],ans;
int l,r,num,Time;
int n,m,bs;
bool cmp(node1 a,node1 b) {
if (be[a.l]!=be[b.l]) return a.l<b.l;
if (be[a.r]!=be[b.r]) return a.r<b.r;
return a.ti<b.ti;
}
inline void update1(int col,int val) {
cnt[col]+=val;
if (val==-1) ans-=(cnt[col]==0);
else ans+=(cnt[col]==1);
}
inline void update2(int po,int newc) {
if (l<=po && po<=r) update1(color[po],-1),update1(newc,1);
color[po]=newc;
}
int main() {
scanf("%d%d",&n,&m);bs=pow(n,0.666667);
for (int i=1;i<=n;i++) {
scanf("%d",&color[i]);
cc[i]=color[i];
be[i]=(i-1)/bs+1;
}
for (int i=1;i<=m;i++) {
char op=0;int x,y;
while (op!='Q'&&op!='R') op=getchar();
scanf("%d%d",&x,&y);
if (op=='Q') query[++num]=(node1){x,y,Time,num};
else change[++Time]=(node2){x,y,cc[x]},cc[x]=y;
}
sort(query+1,query+num+1,cmp);
Time=0;
l=1,r=0;//先把区间记空
for (int i=1;i<=num;i++) {
while (Time<query[i].ti) update2(change[Time+1].x,change[Time+1].newc),Time++;
while (Time>query[i].ti) update2(change[Time].x,change[Time].oldc),Time--;
while (l>query[i].l) update1(color[l-1],1),l--;
while (r<query[i].r) update1(color[r+1],1),r++;
while (l<query[i].l) update1(color[l],-1),l++;
while (r>query[i].r) update1(color[r],-1),r--;
Ans[query[i].ID]=ans;
}
for (int i=1;i<=num;i++) printf("%d\n",Ans[i]);
return 0;
}

3.树形莫队
用栈维护当前节点作为父节点,访问它的子节点,当栈顶到父节点的距离大于blocksize时,弹出这部分元素分为一块。根节点附近剩下的一坨点,最后再分一块。
用vis[x]判断是否在区间内,注意指针移动对LCA的影响。
关于指针移动的关键代码:

1
2
3
4
5
6
7
8
9
void Run(int u) {
if (vis[u]) Data.Del(a[u]),vis[u]=0;
else Data.Add(a[u]),vis[u]=1;
}
void move(int x,int y) {
if (deep[x]<deep[y]) swap(x,y);
while (deep[x]>deep[y]) Run(x),x=fa[x][0];
while (x!=y) Run(x),Run(y),x=fa[x][0],y=fa[y][0];
}

https://www.luogu.org/problemnew/show/P4703
题意:给出平面上的n个圆,求没有被覆盖的点坐标。不存在输出GG。

此题的解空间比较平滑,使用爬山无大碍
但是要注意新随机出来的点坐标范围,不能太大也不能太小

1.爬山

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
const double r=0.8;
double R,RR;
double xx,yy;
struct node {
double x,y;
} a[15];
int n,l;
inline double Ran(double x) {
return rand()%10001/10000.0*x;
}
inline int calc(double xx,double yy) {
int cnt=0;
for (int i=1;i<=n;i++) {
if ((a[i].x-xx)*(a[i].x-xx)+(a[i].y-yy)*(a[i].y-yy)<R*R+1e-6) cnt++;
}
return cnt;
}
inline void getnew(double &nx,double &ny) {
double tmp=max((double)l,RR);
do {
nx=xx+Ran(2*tmp)-tmp;
} while (nx<0||nx>l);
do {
ny=yy+Ran(2*tmp)-tmp;
} while (ny<0||ny>l);
RR*=r;
}
void ps() {
xx=Ran(l),yy=Ran(l);
for (int i=1;i<=1e3;i++) {
if (calc(xx,yy)==0) return;
double nx,ny;
getnew(nx,ny);
double dE=calc(nx,ny)-calc(xx,yy);
if (dE<=0) xx=nx,yy=ny;
}
}
int main() {
scanf("%d%d",&n,&l);R=(double)l/n;RR=l;
for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
srand(time(0));
for (int i=1;i<=100;i++) {
ps();
if (calc(xx,yy)==0) {
printf("%lf %lf",xx,yy);
return 0;
}
}
printf("GG");
return 0;
}

2.退火
此题接受劣解概率较小,注意调参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
const double T_min=1e-8;
const double r=0.98;
double T=1,R,RR;
double xx,yy;
struct node {
double x,y;
} a[15];
int n,l;
inline double Ran(double x) {
return rand()%10001/10000.0*x;
}
inline int calc(double xx,double yy) {
int cnt=0;
for (int i=1;i<=n;i++) {
if ((a[i].x-xx)*(a[i].x-xx)+(a[i].y-yy)*(a[i].y-yy)<R*R+1e-6) cnt++;
}
return cnt;
}
inline void getnew(double &nx,double &ny) {
double tmp=max((double)l,RR);
do {
nx=xx+Ran(2*tmp)-tmp;
} while (nx<0||nx>l);
do {
ny=yy+Ran(2*tmp)-tmp;
} while (ny<0||ny>l);
RR*=r;
}
void mnth() {
xx=Ran(l),yy=Ran(l);
while (T>T_min) {
if (calc(xx,yy)==0) return;
double nx,ny;
getnew(nx,ny);
double dE=calc(nx,ny)-calc(xx,yy);
if (dE<=0) xx=nx,yy=ny;
else if (exp(-dE/T)>Ran(1)) xx=nx,yy=ny;
T=r*T;
}
}
int main() {
scanf("%d%d",&n,&l);R=(double)l/n;RR=l;
for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
srand(time(0));
mnth();
if (calc(xx,yy)==0) printf("%lf %lf",xx,yy);
else printf("GG");
return 0;
}

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素(不复原),你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

看到题目第一反应,树状数组乱搞?
位置线段树套权值树状数组?爆空间!

solution:
1.分块树状数组暴力,不讲
2.树套树
已知有两种写法
位置树状数组,权值线段树
权值树状数组,位置线段树
这里写权值树状数组,位置线段树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxsize 6000005
//动态开点,具体多小试试看
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
int root[maxsize],lson[maxsize],rson[maxsize],sum[maxsize],sz;
int val[100005],pos[100005];
int bb[100005],as[100005];//before_bigger after_smaller
LL ans;
int n,m;
inline int read() {
int sum=0;char ch=0;
while (ch>'9' || ch<'0') ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
inline void update1(int x) {
for (;x<=n;x+=lowbit(x)) root[x]++;
}
inline int getsum1(int x) {
int sum=0;
for (;x>0;x-=lowbit(x)) sum+=root[x];
return sum;
}
int query(int pp,int l,int r,int ll,int rr) {
if (pp==0) return 0;
if (l==ll && r==rr) return sum[pp];
int mid=(l+r)>>1;
if (rr<=mid) return query(lson[pp],l,mid,ll,rr);
if (ll>mid) return query(rson[pp],mid+1,r,ll,rr);
return query(lson[pp],l,mid,ll,mid)+query(rson[pp],mid+1,r,mid+1,rr);
}
void update(int &pp,int l,int r,int ll) {
if (pp==0) {
pp=++sz;
if (sz>=maxsize) puts("MLE");
}
sum[pp]++;
if (l==r && l==ll) return;
int mid=(l+r)>>1;
if (ll<=mid) update(lson[pp],l,mid,ll);
else update(rson[pp],mid+1,r,ll);
}
inline int query_bb(int v,int p) {//值比它大,在它前面
int sum1=0,sum2=0,x=n;
for (;v;v-=lowbit(v)) sum1+=query(root[v],1,n,1,p);
for (;x;x-=lowbit(x)) sum2+=query(root[x],1,n,1,p);
return sum2-sum1;
}
inline int query_as(int v,int p) {//值比它小,在它后面
int sum=0;
for (;v;v-=lowbit(v)) sum+=query(root[v],1,n,p,n);
return sum;
}
inline void update2(int v,int p) {
for (;v<=n;v+=lowbit(v)) update(root[v],1,n,p);
}
int main() {
n=read();m=read();
for (int i=1;i<=n;i++) {
val[i]=read();
pos[val[i]]=i;
bb[i]=getsum1(n)-getsum1(val[i]);//借用一下root[]
ans+=bb[i];
update1(val[i]);
}
memset(root,0,sizeof(root));
for (int i=n;i>0;i--) {
as[i]=getsum1(val[i]-1);
update1(val[i]);
}
memset(root,0,sizeof(root));
while (m--) {
printf("%lld\n",ans);
int v=read(),p=pos[v];
ans-=(bb[p]+as[p]-query_as(v-1,p+1)-query_bb(v,p-1));
update2(v,p);
}
return 0;
}

3.cdq分治
这是个锅,过几天补上

经典的三维偏序问题
排序+cdq分治+树状数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int a,b,c,w,ans;
} q[100005],q1[100005];
int c[200005];
int t[100005];
int n,nn,k;
inline int read() {
int sum=0;char ch=0;
while ((ch>'9' || ch<'0')&&(ch!='-')) ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
bool cmp(node A,node B) {
if (A.a!=B.a) return A.a<B.a;
if (A.b!=B.b) return A.b<B.b;
return A.c<B.c;
}
inline int lowbit(int x) {return x&(-x);}
inline void update(int x,int val) {
for (;x<=k;x+=lowbit(x)) c[x]+=val;
}
inline int getsum(int x) {
int sum=0;
for (;x>0;x-=lowbit(x)) sum+=c[x];
return sum;
}
inline void clear(int x) {
for (;x<=k;x+=lowbit(x)) {
if (!c[x]) break;
c[x]=0;
}
}
void cdq(int l,int r) {
if (l==r) return;
int mid=(l+r)>>1;
cdq(l,mid);cdq(mid+1,r);
int s1=l,s2=mid+1,s3=l-1;
while (s1<=mid && s2<=r) {
if (q[s1].b<=q[s2].b) {
update(q[s1].c,q[s1].w);
q1[++s3]=q[s1];s1++;
}
else {
q[s2].ans+=getsum(q[s2].c);
q1[++s3]=q[s2];s2++;
}
}
while (s1<=mid) q1[++s3]=q[s1],s1++;
while (s2<=r) {
q[s2].ans+=getsum(q[s2].c);
q1[++s3]=q[s2],s2++;
}
for (int i=l;i<=r;i++) {
clear(q1[i].c);
q[i]=q1[i];
}
}
int main() {
n=read();read();
for (int i=1;i<=n;i++) {
q1[i].a=read();q1[i].b=read();q1[i].c=read();
k=max(k,q1[i].c);
}
sort(q1+1,q1+n+1,cmp);
int cnt=0;
for (int i=1;i<=n;i++) {
cnt++;
if (q1[i].a!=q1[i+1].a || q1[i].b!=q1[i+1].b || q1[i].c!=q1[i+1].c)
q[++nn]=q1[i],q[nn].w=cnt,cnt=0;
}
cdq(1,nn);
for (int i=1;i<=nn;i++) t[q[i].ans+q[i].w-1]+=q[i].w;
for (int i=0;i<n;i++) printf("%d\n",t[i]);
return 0;
}

有N个位置,M个操作。
1 a b c形式,表示在第a个位置到第b个位置,每个位置加入一个数c。
2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

区间的第k大值有一种二分的做法。
二分答案mid,计算出区间内>mid的值有多少个。
若数量小于C,则ans<mid,否则ans>=mid。

考虑离线做法,将所有询问一起二分。
每次二分扫描所有操作,维护一个线段树记录在一段区间内有多少个>mid的数。
对于一个询问,直接查询在区间内的>a的数量即可知道ans<mid或>=mid。
但是操作数量也很多,如果每次二分扫描所有操作也会超时。

操作也可以分治,根据c与mid的大小关系进行分治。将操作序列整体二分。
把c<=mid的修改放到左边,反之放在右边。
将ans<mid的的询问放到左边,反之放到右边。
实现时,若用树状数组,清空是把加上去的减回来;若用线段树,打标记清空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
struct node {
int op,L,R,idx;
LL val;
} qu[50005],q1[50005],q2[50005];
int ans[50005],cnt;
LL c1[50005],c2[50005];
int n,m;
inline LL read() {
LL sum=0;char ch=0;int f=1;
while ((ch>'9' || ch<'0')&&(ch!='-')) ch=getchar();
if (ch=='-') f=-1,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum*f;
}
inline int lowbit(int x) {return x&(-x);}
inline void update(int x,LL val) {
LL tmp=x*val;
for (;x<=n;x+=lowbit(x)) c1[x]+=val,c2[x]+=tmp;
}
inline LL getsum(int x) {
LL a=0,b=0,tmp=x+1;
for (;x>0;x-=lowbit(x)) a+=c1[x],b+=c2[x];
return a*tmp-b;
}
inline void Update(int l,int r,LL val) {
update(l,val);
update(r+1,-val);
}
inline LL query(int l,int r) {
return getsum(r)-getsum(l-1);
}
void solve(int l,int r,int s,int t) {
if (s>t) return;
if (l==r) {
for (int i=s;i<=t;i++)
if (qu[i].op==2) ans[qu[i].idx]=l;
return;
}
int mid=(l+r)>>1;
int s1=0,s2=0;
for (int i=s;i<=t;i++) {
if (qu[i].op==1) {
if (qu[i].val>mid) {
Update(qu[i].L,qu[i].R,1);
q2[++s2]=qu[i];
}
else q1[++s1]=qu[i];
}
else {
LL tmp=query(qu[i].L,qu[i].R);
if (tmp<qu[i].val) {
qu[i].val-=tmp;
q1[++s1]=qu[i];
}
else q2[++s2]=qu[i];
}
}
for (int i=s;i<=t;i++) {
if (qu[i].op==1 && qu[i].val>mid) Update(qu[i].L,qu[i].R,-1);
}
for (int i=1;i<=s1;i++) qu[s+i-1]=q1[i];
for (int i=1;i<=s2;i++) qu[t-s2+i]=q2[i];
solve(l,mid,s,s+s1-1);
solve(mid+1,r,s+s1,t);
}
int main() {
n=read();m=read();int mx=0,mn=1e9;
for (int i=1;i<=m;i++) {
qu[i].op=read();qu[i].L=read();
qu[i].R=read();qu[i].val=read();
if (qu[i].op==2) qu[i].idx=++cnt;
else mx=max(mx,(int)qu[i].val),mn=min(mn,(int)qu[i].val);
}
solve(mn,mx,1,m);
for (int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
return 0;
}

给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值
https://www.luogu.org/problemnew/show/P3527

先简单介绍一下整体二分:
当询问可以二分答案,且多组询问可以离线的时候,可以考虑整体二分。
因为所有询问要一起二分答案,所以把操作分治成一小块一小块,相应的询问放到相应的小块判断可行性。

这道题十分特殊,所有询问都在修改后面,而且二分的就是操作时间,可以这么处理。
每一次计算出mid时刻每个国家已经收集的数量,把所有能够完成国家的放在左边,所有不能完成的国家放在右边。左边继续二分mid以下的时间,右边继续二分mid以上的时间。
判断能否完成,用树状数组朝着mid时刻暴力加减即可,不能清零。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long LL;
LL c[300005];
vector<int> G[300005];
int L[300005],R[300005],val[300005];
int P[300005],ans[300005];
int q[300005],q1[300005],q2[300005];
int n,m,k,now=0;
inline int read() {
int sum=0;char ch=0;
while (ch>'9' || ch<'0') ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
inline int lowbit(int x) {
return x&-x;
}
inline void add(int x,int v) {
for (;x<=m;x+=lowbit(x)) c[x]+=v;
}
inline LL query(int x) {
LL tmp=0;
for (;x>0;x-=lowbit(x)) tmp+=c[x];
return tmp;
}
inline void update(int l,int r,int v) {
if (l<=r) {
add(l,v);add(r+1,-v);
}
else {
add(1,v);add(r+1,-v);
add(l,v);add(m+1,-v);
}
}
void solve(int l,int r,int s,int t) {//lr流星雨区间 mid答案 st未解决的国家区间
if (s>t) return;
if (l==r) {
for (int i=s;i<=t;i++) ans[q[i]]=l;
return;
}
int mid=(l+r)>>1;
while (now<mid) now++,update(L[now],R[now],val[now]);
while (now>mid) update(L[now],R[now],-val[now]),now--;
int s1=0,s2=0;
for (int i=s;i<=t;i++) {
LL tmp=0;
int x=q[i];
for (int j=0;j<G[x].size();j++) {
tmp+=query(G[x][j]);
if (tmp>=P[x]) break;
}
if (tmp>=P[x]) q1[++s1]=x;
else q2[++s2]=x;
}
for (int i=1;i<=s1;i++) q[s+i-1]=q1[i];
for (int i=1;i<=s2;i++) q[s+s1+i-1]=q2[i];
solve(l,mid,s,s+s1-1);
solve(mid+1,r,s+s1,t);
}
int main() {
n=read();m=read();
for (int i=1;i<=m;i++) G[read()].push_back(i);
for (int i=1;i<=n;i++) {
P[i]=read();
q[i]=i;
}
k=read();
for (int i=1;i<=k;i++) {
L[i]=read();R[i]=read();val[i]=read();
}
solve(1,k+1,1,n);
for (int i=1;i<=n;i++) {
if (ans[i]==k+1) printf("NIE\n");
else printf("%d\n",ans[i]);
}
return 0;
}

学习资源:
https://www.cnblogs.com/fenghaoran/p/7436593.html

本题谢绝转载

五十分很送,七十分也水。

斐波那契数列有两个性质:
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
打出了有史以来最长的代码,下面附的还是精简版。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#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;
}

只是对拍了一下,正确性不能保证,但应该没问题。

https://blog.csdn.net/clove_unique/article/details/54884437

https://www.cnblogs.com/gtarcoder/p/4888973.html
(修正第二个链接中的内容:第三幅图流量上限全部-1,而且不存在可行流)