树状数组
来源:互联网 发布:淘宝靠谱美国代购 编辑:程序博客网 时间:2024/06/09 20:19
(我又来写题解了~~~)
树状数组(二叉搜索树)
定义:
由此可见,这个数组像一棵树一样,故曰树状数组(实际上本来就是一棵树)。
下面解释
1.基本操作:
单点更新:
int update(int x,int val){ while (x<=n) { c[x]+=val; x+=lowbit(x); }}
区间查询:
int query(int x){ int ans = 0; while (x) { ans += c[x]; x-=lowbit(x); }}
实际上也可以区间更新,区间查询。记录一个新数组
例题
- Codevs 1082
题目:区间修改,区间查询。
代码:
#include<cstdio>#include<algorithm>const int size = 200005;using namespace std;typedef long long ll; ll del[size],del2[size],num[size];ll n , m;ll dodo ,l ,r,val;ll lowbit(ll x){return (x &(-x));}void update(ll x,ll val,ll *arr){ while (x<=n) { arr[x] += val; x += lowbit(x); }}ll query(ll x,ll *arr){ ll ans = 0; while(x) { ans += arr[x]; x -= lowbit(x); } return ans;}int main(){ scanf("%lld" , &n); for (ll i = 1;i<=n;i++) {scanf("%lld" , &num[i]); update(i,num[i] - num[i-1],del); update(i,(i-1) * (num[i] - num[i-1]) ,del2); } scanf("%lld",&m); for (ll i =1;i <=m ;i++) { scanf("%lld",&dodo); if (dodo == 1) { scanf("%lld%lld%lld",&l,&r,&val); update(l,val,del); update(r+1,-val,del); update(l,val * (l-1),del2); update(r+1,-val * r,del2); } else { scanf("%lld%lld",&l,&r); ll lle = (l-1) * query(l-1,del) - query(l-1,del2); ll rr = r * query(r,del) - query(r,del2); printf("%lld\n",rr-lle); } } return 0;}
- Bzoj 1452: [JSOI2009]Count
题目: 略
题解:水题啊,注意到
代码:
#include<cstdio>#include<algorithm>#include<cstring>int n,m,q;//n 对应 x //m 对应 y int c[101][310][310];int mat[310][310];int lowbit(int x){return (x&(-x));}using namespace std;void update(int x,int y,int pre,int now){ int x0 = x;int y0 = y; while (x0<=n) { y0 = y; while (y0 <= m) { c[now][x0][y0]++; y0 += lowbit(y0); } x0 += lowbit (x0); } x0 = x; y0 = y; while (x0<=n) { y0 = y; while (y0 <= m) { c[pre][x0][y0]--; y0 += lowbit(y0); } x0 += lowbit (x0); }}int query(int val,int x,int y){ int ans = 0; int x0 = x;int y0 = y; while (x0) { y0 = y; while (y0) { ans += c[val][x0][y0]; y0 -= lowbit(y0); } x0 -= lowbit (x0); } return ans;}int main(){ scanf("%d%d",&n,&m); memset(c,0,sizeof c); for (int x = 1;x <= n ;x++) for (int y = 1;y <= m;y++) { scanf("%d",&mat[x][y]); update(x,y,0,mat[x][y]); } scanf("%d",&q); while (q--) { int dodo; scanf("%d",&dodo); if (dodo == 1) { int x,y,val; scanf("%d%d%d",&x,&y,&val); update(x,y,mat[x][y],val); mat[x][y] = val; }else if (dodo == 2) { int x1,x2,y1,y2,val; scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&val); int ans = query(val,x2,y2)+ query(val,x1-1,y1-1) - query(val,x2,y1-1)- query(val,x1-1, y2); printf("%d\n",ans); } } return 0;}
- bzoj 2743[HEOI2012]采花
题目:
萧芸斓是Z国的公主,平时的一大爱好是采花。
今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。花园足够大,容纳了n朵花,花有c种颜色(用整数1-c表示),且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了m个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。
题解:
实际上就是问你区间上有几个落单的数。那么我们可以打一下标记,先把询问按左端点排序,然后记录每个数的下一个与其值相等的数的位置next ,然后先把每个值第二个加入树状数组,接着做时,就遇到一个i ,去掉next[i] ,加上next[next[i]] 。(因为这样就可以使如果只有一个,一正一负刚好可以抵了,反之,也对,把第二个也加上是考虑了第零个)。
代码:
#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int maxn = 1000110;int lowbit(int x){ return (x & (-x));}int n , m ,ww;int c[maxn] , a[maxn] , ans[maxn] , next[maxn] , pre[maxn ] ;bool vis[maxn];struct q{ int l ,r ,id; bool operator < (const q &cx) const{ if (l == cx.l) return r < cx.r; return l < cx.l; }}qq[maxn];void update (int x,int del){ while (x <= n) { c[x] += del; x += lowbit(x); }}int query(int x){ if (x==0) return 0; int ans = 0; while (x) { ans += c[x]; x -= lowbit (x); } return ans;}int main(){ scanf("%d%d%d",&n,&ww,&m); for (int i = 1 ;i <= n ;i++) scanf("%d" , &a[i]); for (int i = 1;i <= m;i++) scanf("%d%d", &qq[i].l, &qq[i].r), qq[i].id = i; sort(qq + 1 , qq + 1 + m); memset(vis,0,sizeof vis); for (int i = 1;i <= n;i++) { if (pre[ a[i] ]) next[ pre[ a[i] ] ] = i; else vis[i] = 1; pre[ a[i] ] = i; } for (int i = 1;i <= n;i++) if (vis[i] && next[i]) update(next[i],1); int nowr = 1; for(int i = 1;i <= m;i++) { while (nowr < qq[i].l) { if (next[nowr]) { update(next[nowr] , -1); if (next [ next [ nowr ] ]) update(next[ next[nowr] ] , 1); } nowr++; } ans[qq[i].id] = query(qq[i].r) ; } for (int i = 1;i <= m;i++) printf("%d\n",ans[i]); return 0;}
- bzoj 1878: [SDOI2009]HH的项链
题解:
与上题相似,但这题按右端点排序。我们记录一个a[i] 表示i 是不是当前区间内x[i] 这个值最靠右的(x 是原序列),然后就统计一下就完了。
代码:
#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int maxn = 200110;int lowbit(int x){ return (x & (-x));}int n , m;int c[maxn] , a[maxn] , ans[maxn] , next[maxn] , pre[maxn * 5] ;struct q{ int l ,r ,id; bool operator < (const q &cx) const{ if (r == cx.r) return l < cx.l; return r < cx.r; }}qq[maxn];void update (int x,int del){ while (x <= n) { c[x] += del; x += lowbit(x); }}int query(int x){ if (x==0) return 0; int ans = 0; while (x) { ans += c[x]; x -= lowbit (x); } return ans;}int main(){ scanf("%d",&n); for (int i = 1 ;i <= n ;i++) scanf("%d" , &a[i]); scanf("%d" , &m); for (int i = 1;i <= m;i++) scanf("%d%d", &qq[i].l, &qq[i].r), qq[i].id = i; sort(qq + 1 , qq + 1 + m); for (int i = 1;i <= n;i++) { if (pre[ a[i] ]) next[ pre[ a[i] ] ] = i; pre[ a[i] ] = i; } memset(pre , 0 , sizeof pre); for (int i = 1;i <= n;i++) if (next[i]) pre[ next[i] ] = i; int nowr = 0; for(int i = 1;i <= m;i++) { while (nowr < qq[i].r) { nowr++; if (pre[nowr]) update(pre[nowr] , -1); if (nowr) update(nowr , 1); } ans[qq[i].id] = query(qq[i].r) - query(qq[i].l-1); } for (int i = 1;i <= m;i++) printf("%d\n",ans[i]); return 0;}
- bzoj 4240: 有趣的家庭菜园
题目:略
题解:
(这题好啊)*最小交换次数就是最后序列的关于原位置的逆序对*,然后我们可以证明最后数列是一个先增后减的样子。(好不容易遇到一个我会证的,当然要证一证)。如下:首先最大的可以随便放,然后我们考虑他左边的第一个数
总结:这道题最大的转点在于:最小交换次数就是最后序列的关于原位置的逆序对这一结论,一定要记熟。
代码:(压了行的,别打我)
#include<cstdio>#include<algorithm>using namespace std;const int maxn = 300010; inline int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }return x;}int c[maxn],n,answer = 0;struct gra{int h,id;bool operator < (const gra &d) const {return h > d.h;}}a[maxn];int lowbit(int x){return x& (-x);}int query(int x){int ans = 0 ;while (x){ans+=c[x] ;x-=lowbit(x);}return ans;}void update(int x,int val){while (x <= n){c[x]+=val;x +=lowbit(x);}}int main(){n=read();for (int i = 1;i <= n ;i ++){a[i].h=read();a[i].id = i;}sort(a + 1,a + 1 + n);int head=1; long long ans=0; for (int i=1; i<=n; i++){if (a[i].h!=a[i-1].h)while (head<i) update(a[head++].id , 1); int tmp=query(a[i].id); ans+=min(tmp,head-1-tmp);}printf("%lld",ans);return 0;}
- bzoj 2131 : 免费的馅饼
(数据小点还可以dp的,气)。这个我实在是看的别人的,这里推荐一个写得好的:
http://m.blog.csdn.net/FromATP/article/details/64133191
代码:
#include<cstdio>#include<algorithm>const int size = 100010;using namespace std;int c[size] , n ,w ,y[size],f[size],answ=0,cnt;struct th{ int v,t,loc,w1,w2; bool operator < (const th &de) const{ if (w1==de.w1) return w2<de.w2;return w1<de.w1; }}ob[size];int lowbit(int x){return (x&(-x));}int query(int x){ int ans=0; while (x) {ans = max(c[x],ans); x -= lowbit(x);} return ans;}void update(int x,int val){ while (x<=n) {c[x] = max(c[x],val); x+=lowbit(x);}}int main(){ scanf("%d%d",&w,&n); for (int i = 1;i<=n;i++){ scanf("%d%d%d",&ob[i].t,&ob[i].loc,&ob[i].v); ob[i].w1 = 2 * ob[i].t + ob[i].loc; ob[i].w2 = 2 * ob[i].t - ob[i].loc; y[++cnt] = ob[i].w2; } sort(y + 1 , y + 1 + n); cnt = unique (y + 1 , y + 1 + n) - y - 1; for (int i = 1;i<=n;i++) ob[i].w2 = lower_bound(y+1,y+1+cnt,ob[i].w2)-y; sort(ob + 1,ob + 1 + n); for (int i = 1;i<=n;i++) { f[i] = ob[i].v + query(ob[i].w2); answ = max(answ,f[i]); update(ob[i].w2 , f[i]); } //for (int i =1;i<=n;i++) printf("%d",answ); return 0;}
结语
树状数组大法好,好写又好调!!!
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- android如何获取操作外置sd卡的权限
- 第二周三种传递方法
- uva 1395Slim Span
- 【androidx86 5.1.1】Android HttpClient请求过程解析(下)
- 当creator遇上protobufjs—pbkiller插件
- 树状数组
- with引起的类的扩充
- Java学习第五天
- 深入理解 Kotlin coroutine (二)
- Leetcode之Pascal's Triangle II 问题
- 第二周项目2---程序的多文件组织
- 前缀,中缀,后缀表达式简介及转换 -- C# 简化小例
- java字体设置,包括大小,颜色,加粗,下划线,对齐,斜体的设置
- 两个surfaceview重叠显示