(我又来写题解了~~~)
定义: c[i] 维护的是 [i,i−lowbit(i)+1] 这段区间的值,这个 lowbit 等会解释。树状数组,他的时间复杂度为 O(logN) ,而且常数小,还好写,简直就是oi中的大法。并且我们可以“感受”到,如果只有一般的修改与一般的询问最大值,前缀和等,都完全可以替代线段树。(当然,有些时候他也替代不了线段树,比如主席树时)。下面来直观看看这个数组c的管辖范围。
由此可见,这个数组像一棵树一样,故曰树状数组(实际上本来就是一棵树)。
下面解释 lowbit(x) 的含义: lowbit(x) 是最大的 2k ,使得 2k 是 x 的约数,而 2(k+1) 不是其约数。即 x 的约数中最大的二的幂。也可以说是 x 在二进制时最后一个 1 所代表的值。
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);
}
}
实际上也可以区间更新,区间查询。记录一个新数组 a[i]=x[i]−x[i−1] (原数组为 x ),那么修改时修改差分,然后差分的前缀和就是单点的数。而区间查询则需要推公式,推出来为 x×∑a[i]−∑a[i]×(i−1) ;那么就只需要再记录一个新的数组记录 a[i]×(i−1) 。
#include
#include
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;
}
题目: 略
题解:水题啊,注意到 c 的值很小, m , n 的也很小,那么我们就记录树状数组 c[d][m][n] 表示 d 这个数在 (0,0)—(m,n) 这个矩形内出现次数,然后就完了。
代码:
#include
#include
#include
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;
}
#include
#include
#include
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;
}
#include
#include
#include
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;
}
题目:略
题解:
(这题好啊)*最小交换次数就是最后序列的关于原位置的逆序对*,然后我们可以证明最后数列是一个先增后减的样子。(好不容易遇到一个我会证的,当然要证一证)。如下:首先最大的可以随便放,然后我们考虑他左边的第一个数 Ai ,那么 Ai 右边有比他大的了,所以 Ai 左边都不能比 Ai 大,所以 Ai 是 [1,i] 最大的,同理可证 Ai−1 也是 [1,Ai−1] 最大的,于是在最大的以左为单增,同理可证右边为单减。好了,接着把草从大到小排一次序,那么我们新放的数一定在已经放的数的最左边或最右边,然后开始贪心,每次加入其位置,然后看是放左边还是右边。因为大的已经放了,较小的无论怎么放都不会影响之前的大的,所以可以贪心。
总结:这道题最大的转点在于:最小交换次数就是最后序列的关于原位置的逆序对这一结论,一定要记熟。
代码:(压了行的,别打我)
#include
#include
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 (head1); int tmp=query(a[i].id);
ans+=min(tmp,head-1-tmp);}printf("%lld",ans);return 0;}
(数据小点还可以dp的,气)。这个我实在是看的别人的,这里推荐一个写得好的:
http://m.blog.csdn.net/FromATP/article/details/64133191
代码:
#include
#include
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 w2return w1int 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;
}
树状数组大法好,好写又好调!!!