【LuoguP3038/[USACO11DEC]牧草种植Grass Planting】树链剖分+树状数组【树状数组的区间修改与区间查询】

模拟题,可以用树链剖分+线段树维护。

但是学了一个厉害的。。树状数组的区间修改与区间查询。。

分割线里面的是转载的:

--------------------------------------------------------------------------------

[ 3 ]  上面都不是重点……重点是树状数组的区间修改+区间查询 这个很好玩 其实也挺简单

首先依旧是引入delta数组 delta[i]表示区间 [i, n] 的共同增量 于是修改区间 [l, r] 时修改 delta[l] 和 delta[r + 1] 即可(就是差分的思路)

查询的时候是查询区间 [l, r] 的和 即sum[r] - sum[l - 1] 所以现在的问题是求sum[i]

1 sum[i] = a[1]+...+a[i] + delta[1]*i + delta[2]*(i - 1) + delta[3]*(i - 2)+...+delta[i]*1   // a[i]为原始数组
2        = sigma( a[x] ) + sigma( delta[x]  *  (i + 1 - x) )
3        = sigma( a[x] ) + (i + 1) * sigma( delta[x] ) - sigma( delta[x] * x ) 

其中 sigma( a[x] ) 是可以预处理出来的 于是只需要维护 delta[x] 与 delta[x] * x 的前缀和(作为两个树状数组就可以了)

为了试验这个方法我专门去找了之前写线段树挂了好久的例题 = = codevs1082 线段树练习3

然后交树状数组的代码是 324ms 内存5M过了 线段树是1027ms 13M 如果去掉读入优化的话代码会更短。

--------------------------------------------------------------------------------------------------------------------------

转自:http://blog.csdn.net/qq_21841245/article/details/43956633

很好。。这题本机测系统暴栈了。。交上去才A。。

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<ctime>
  6 #include<queue>
  7 using namespace std;
  8
  9 const int N=2*100010,S=30,D=20;
 10 struct node{
 11     int x,y,next;
 12 }a[2*N];
 13 struct trnode{
 14     int l,r,lc,rc,d;
 15 }t[2*N];
 16 int n,m,len,num,first[N],dep[N],f[N][S],tot[N],zs[N],dfn[N],top[N],c0[N],c1[N],delta[N];
 17 char s[10];
 18
 19 void add(int x,int d)
 20 {
 21     for(int i=x;i<=n;i+=(i&(-i))) c0[i]+=d,c1[i]+=d*x;
 22 }
 23
 24 int getsum(int x)
 25 {
 26     int a0=0,a1=0;
 27     for(int i=x;i>=1;i-=(i&(-i))) a0+=c0[i],a1+=c1[i];
 28     return a0*(x+1)-a1;
 29 }
 30
 31
 32 void ins(int x,int y)
 33 {
 34     a[++len].x=x;a[len].y=y;
 35     a[len].next=first[x];first[x]=len;
 36 }
 37
 38 void dfs(int x,int fa)
 39 {
 40     dep[x]=dep[fa]+1;
 41     f[x][0]=fa;
 42     tot[x]=1;
 43     zs[x]=0;
 44     for(int i=first[x];i;i=a[i].next)
 45     {
 46         int y=a[i].y;
 47         if(y==fa) continue;
 48         dfs(y,x);
 49         tot[x]+=tot[y];
 50         if(zs[x]==0 || tot[y]>tot[zs[x]]) zs[x]=y;
 51     }
 52 }
 53
 54 void find_top(int x,int fa)
 55 {
 56     dfn[x]=++num;
 57     if(zs[x])
 58     {
 59         top[zs[x]]=top[x];
 60         find_top(zs[x],x);
 61     }
 62     for(int i=first[x];i;i=a[i].next)
 63     {
 64         int y=a[i].y;
 65         if(y==fa || y==zs[x]) continue;
 66         top[y]=y;
 67         find_top(y,x);
 68     }
 69 }
 70
 71 int solve(int x,int y,int tmp)
 72 {
 73     int tx=top[x],ty=top[y],ans=0;
 74     while(tx!=ty)
 75     {
 76         if(dep[tx]<dep[ty]) swap(x,y),swap(tx,ty);
 77         if(tmp==0)
 78         {
 79             add(dfn[tx],1);
 80             add(dfn[x]+1,-1);
 81         }
 82         else ans+=getsum(dfn[x])-getsum(dfn[tx]-1);
 83         x=f[top[x]][0];tx=top[x];
 84     }
 85     if(x==y) return ans;
 86     else
 87     {
 88         if(dep[x]<dep[y]) swap(x,y);
 89         if(tmp==0)
 90         {
 91             add(dfn[y]+1,1);
 92             add(dfn[x]+1,-1);
 93         }
 94         else ans+=getsum(dfn[x])-getsum(dfn[y]);
 95         return ans;
 96     }
 97 }
 98
 99 void lca_init()
100 {
101     for(int j=1;j<=D;j++)
102         for(int i=1;i<=n;i++)
103             f[i][j]=f[f[i][j-1]][j-1];
104 }
105
106 int lca_query(int x,int y)
107 {
108     if(dep[x]<dep[y]) swap(x,y);
109     for(int i=D;i>=0;i--)
110     {
111         if(f[x][i]==0) continue;
112         if(dep[f[x][i]]>=dep[y]) x=f[x][i];
113     }
114     if(x==y) return x;
115     for(int i=D;i>=0;i--)
116     {
117         if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
118     }
119     return f[x][0];
120 }
121
122 int main()
123 {
124     freopen("a.in","r",stdin);
125     // freopen("a.out","w",stdout);
126     // freopen("grassplant.in","r",stdin);
127     // freopen("grassplant.out","w",stdout);
128     scanf("%d%d",&n,&m);
129     int x,y,z;len=0;num=0;
130     memset(first,0,sizeof(first));
131     memset(f,0,sizeof(f));
132     memset(c0,0,sizeof(c0));
133     memset(c1,0,sizeof(c1));
134     memset(dep,0,sizeof(dep));
135     memset(tot,0,sizeof(tot));
136     memset(zs,0,sizeof(zs));
137     memset(dfn,0,sizeof(dfn));
138     for(int i=1;i<n;i++)
139     {
140         scanf("%d%d",&x,&y);
141         ins(x,y);
142         ins(y,x);
143     }
144     dfs(1,0);
145     top[1]=1;find_top(1,0);
146     // for(int i=1;i<=n;i++)
147     // {
148         // printf("i = %d  dep = %d  zs = %d  tot = %d  dfn = %d  top = %d\n",i,dep[i],zs[i],tot[i],dfn[i],top[i]);
149     // }
150     lca_init();
151     for(int i=1;i<=m;i++)
152     {
153         scanf("%s",s);
154         scanf("%d%d",&x,&y);
155         if(s[0]==‘P‘)
156         {
157             z=lca_query(x,y);
158             solve(x,z,0);
159             solve(y,z,0);
160         }
161         else
162         {
163             printf("%d\n",solve(x,y,1));
164         }
165     }
166     return 0;
167 }
时间: 2024-08-24 21:11:59

【LuoguP3038/[USACO11DEC]牧草种植Grass Planting】树链剖分+树状数组【树状数组的区间修改与区间查询】的相关文章

P3038 [USACO11DEC]牧草种植Grass Planting

P3038 [USACO11DEC]牧草种植Grass Planting 题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional roads, such that there is exactly one path between any two pastures. Bessie, a cow who loves her grazing time, often compl

AC日记——[USACO11DEC]牧草种植Grass Planting 洛谷 P3038

题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional roads, such that there is exactly one path between any two pastures. Bessie, a cow who loves her grazing time, often complains about how there is no grass on t

[USACO11DEC] 牧草种植Grass Planting

https://www.luogu.org/problemnew/show/3038 将为边加权转化成为点加权 对于每条边,我们将其深度更大的端点加权,则区间 [l, r] 的加权(或询问)即为去掉深度最小的点,给其余点加权(或询问). 1)最后一次树剖查询时,当前两点 x,y 必定在同一重链上(y 的深度小于 x): 2)根据处理前的 dfs 序,我们是优先遍历重儿子,那么重儿子在线段树中的编号一定紧邻其父节点(即为其父节点编号 + 1),则我们要忽略深度最小的那个点 y ,即为处理区间 [

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个dis非常不爽.显然s~t的路径有s~lca和lca~t组成.令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x

【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA

引用题解: http://blog.csdn.net/popoqqq/article/details/38823457 题目大意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)].(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和) 这题看见了直接卡壳...然后

【块状树】【树链剖分】【线段树】bzoj3531 [Sdoi2014]旅行

离线后以宗教为第一关键字,操作时间为第二关键字排序. <法一>块状树,线下ac,线上tle…… #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<queue> using namespace std; queue<int>q; int f,c; inline void R(int &x){ c=0;f=1;

【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count

很早之前用树链剖分写过,但是代码太长太难写,省选现场就写错了. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 #define lson rt<<1,l,m 6 #define rson rt<<1|1,m+1,r 7 #define maxn 60000 8 int n,m,u,v; 9 int V[maxn],Next[

BZOJ 2819 Nim 树链剖分/DFS序+LCA+树状数组

题意:给定一棵树,每个节点是一堆石子,给定两种操作: 1.改变x号节点的石子数量 2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略 Nim游戏有必胜策略的充要条件是所有堆的石子数异或起来不为零 这题首先一看就是树链剖分 然后题目很善良地告诉我们深搜会爆栈 于是我们可以选择广搜版的树链剖分 BFS序从左到右是深搜,从右到左是回溯,一遍BFS就够 单点修改区间查询还可以套用ZKW线段树 不过这题其实不用这么麻烦 有更简单的办法 详见 http://dzy493941464.is