【树状数组】树状数组の奇技淫巧专场
来源:互联网 发布:2017最新一手数据 编辑:程序博客网 时间:2024/05/16 07:57
本篇文章将介绍一些非常规形态的树状数组的使用。
一)异或版
树状数组中记录的是一段值异或的结果。
例题:BZOJ2819
题目大意:
给定一棵树,每个节点是一堆石子,给定两种操作:
1.改变x号节点的石子数量
2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略
题解:
既然它只修改点的话,影响到的只是它这棵子树。那么很容易就想到了dfs序。这个子树就是连续一段。先维护每个点dfs开始时和结束时的时间戳。修改的时候先在它自己的开始、结束位置上xor它自己变成零,然后再修改。(x,y)路径上的xor值=query(x的开始) xor query(y的开始) xor lca(x,y)的点权。很好想通。LCA就倍增算一下好了。
代码如下:
#include<iostream>#include<stdio.h>#include<algorithm>#include<string.h>#include<math.h>#define ll long long#define inf 0x7f7f7f7f#define N 500005#define lb(x) (x&-x)using namespace std;ll read(){ ll x=0,f=1; char c=getchar(); while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();} while(c<='9' && c>='0') {x=x*10+c-'0';c=getchar();} return x*f;}int n,m,x,y,v[N],e[N<<1],nex[N<<1],hd[N],tot,ind; int t[N],l[N],r[N],fa[N][20],dep[N];char op[5];void mdy(int x,int v){ while(x<=n) { t[x]^=v; x+=lb(x); }}int query(int x){ int ret=0; while(x) { ret^=t[x]; x-=lb(x); } return ret;}void add(int u,int v){ e[++tot]=v,nex[tot]=hd[u],hd[u]=tot; e[++tot]=u,nex[tot]=hd[v],hd[v]=tot;}void dfs(int u){ l[u]=++ind; for(int i=hd[u];i;i=nex[i]) { if(e[i]==fa[u][0]) continue; fa[e[i]][0]=u; dep[e[i]]=dep[u]+1; dfs(e[i]); } r[u]=ind;}void init(){ for(int i=1;i<=19;i++) for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1];}int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); int t=dep[u]-dep[v]; for(int i=0;i<=18;i++) if((1<<i)&t) u=fa[u][i]; for(int i=18;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; if(u==v) return u; return fa[u][0];}int main(){ n=read(); for(int i=1;i<=n;i++) v[i]=read(); for(int i=1;i<n;i++) add(read(),read()); dfs(1);init(); for(int i=1;i<=n;i++) mdy(l[i],v[i]),mdy(r[i]+1,v[i]); m=read(); for(int i=1;i<=m;i++) { scanf("%s",op); x=read(),y=read(); if(op[0]=='Q') { int t=lca(x,y); int ret=query(l[x])^query(l[y])^v[t]; if(ret) puts("Yes"); else puts("No"); } else { mdy(l[x],v[x]),mdy(r[x]+1,v[x]); v[x]=y; mdy(l[x],v[x]),mdy(r[x]+1,v[x]); } } return 0;}
二)MAX、MIN版
树状数组中记录的是一些值的最大值。
例题:BZOJ3594
题解:
令f[i][j]表示前i个数上升j次的最大LIS
那么有f[i][j]=max{f[k][l]|k < i,l <= j,a[k]+l <= a[i]+j}+1
由于dp方程记录的是最大值,因此树状数组也必须相匹配,记录最大值。
这也提示我们树状数组的变化是很灵活的,要根据需要决定。
代码如下:
#include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<algorithm> #define lb(x) (x&(-x)) using namespace std; int c[6005][505],dp[10005][505],a[10005]; int n,m,ans,mx; void mdy(int x,int y,int z) { for(int i=x;i<=mx+m;i+=lb(i)) for(int j=y;j<=m+1;j+=lb(j)) c[i][j]=max(c[i][j],z); } int query(int x,int y) { int ret=0; for(int i=x;i;i-=lb(i)) for(int j=y;j;j-=lb(j)) ret=max(ret,c[i][j]); return ret; } int main() { scanf("%d%d",&n,&m); ans=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); mx=max(mx,a[i]); } for(int i=1;i<=n;i++) for(int j=m;j>=0;j--) { dp[i][j]=query(a[i]+j,j+1)+1; ans=max(ans,dp[i][j]); mdy(a[i]+j,j+1,dp[i][j]); } printf("%d\n",ans); return 0; }
0 0
- 【树状数组】树状数组の奇技淫巧专场
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 下面哪些机制可以用于进程间通信
- QT之HelloWorld
- 【BZOJ】1022: [SHOI2008]小约翰的游戏John
- 万圣节后的早晨
- 小波变换
- 【树状数组】树状数组の奇技淫巧专场
- Helpers方法
- noi.openjudge 1711 潜伏者答案及其分析
- Java IO 系列源码分析——InputStream和OutputStream
- Java面试系列之HashMap大扫盲汇总
- 【LeetCode】17. Letter Combinations of a Phone Number
- 分页内存和非分页内存区别
- 解决Linux 启动 Nginx报错 :nginx: [emerg] mkdir() "/var/temp/nginx/client" failed (2: No such file or direc
- storm分布式安装