树状数组习题

逆序对数:

链接:https://ac.nowcoder.com/acm/contest/358/D
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

出题人的妹子送了出题人一个手环,这个手环上有 n 个珠子,每个珠子上有一个数。

有一天,出题人和妹子分手了,想把这个手环从两个珠子间切开,并按顺时针顺序展开成一条链。

可以发现,这条链一共有 n 种可能性。求这 n 种可能性的逆序对数之积模 1000000007。

解析:

数据非常大,要进行离散化

先算出第一种情况的逆序对个数,然后再最后的呢个数换到前面的情况

前面比它小的会成逆序对,比他大的会失去逆序对

用树状数组求原数组逆序对数,然后我们模拟将a[1]换到a[n]后面

对于比a[1]后面比a[1]小的数将成新的逆序对,比a[1]的的数将失去逆序对
我们标记a[i]出现次数,然后用前缀和维护数目,实现o(1)查询,遍历累乘即可

ac:

#include
#define ll long long
using namespace std;
#define MAXN 200000+5
#define mod 1000000007
ll a[MAXN],b[MAXN],n;
ll tree[MAXN],vis[MAXN],sum[MAXN];

ll lowbit(ll x)
{
    return x&(-x);  
}

void add(ll x,int val)
{
    for(ll i=x;i<=n;i=i+lowbit(i))
    {
        tree[i]+=val;
        tree[i]=tree[i]%mod;
    }
}

ll get(ll x)
{
    ll ans=0;
    for(ll i=x;i;i=i-lowbit(i))
    {
        ans+=tree[i];
    }
    return ans%mod;
}

int main()
{
    memset(vis,0,sizeof(vis));
    memset(tree,0,sizeof(tree));
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(sum,0,sizeof(sum));
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    ll cnt=unique(b+1,b+1+n)-b-1;
    for(ll i=1;i<=n;i++)
    {
        a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
        vis[a[i]]++;
    }
    for(ll i=1;i<=n;i++)
        sum[i]=sum[i-1]+vis[i];
    ll ans=0;
    for(ll i=1;i<=n;i++)
    {
        add(a[i],1);
        ans+=i-get(a[i]);
        ans=ans%mod;
    }
    ll bans=ans,aq,bq;
    for(ll i=n;i>=2;i--)
    {
        aq=sum[a[i]-1];//比a[i]小的
        bq=n-sum[a[i]];//比a[i]大的
        bans=(bans+aq-bq+mod)%mod;
        ans=ans*bans;
        ans=ans%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

区间小于等于k的数目(离线):

树状数组:

a[i]按val排,b[i]按val(k)排,保存和a[i]的位置,b[i]的顺序,保证单调

每次只add比b[i].val小的a[i].val的位置

每次把比b[i]小的数位置上+1,这样我们直接查询(l,r)的数目,就是(l,r)中小于b[i].val的数目

这里吧所有比b[i]小的数都+1,保证在(l,r)区间内查询到的值是完整的

#include
#define MAXN 100005
#define ll long long
#define lowbit(x) ((x)&(-x))
using namespace std;

int n,m;
int ans[MAXN];

struct node
{
    int id,val;
    friend bool operator <(node a,node b)
    {
        return a.val0;i-=lowbit(i))
        ans+=sum[i];
    return ans;
}

int main()
{
    int t,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(sum,0,sizeof(sum));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].val);
            a[i].id=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].val);
            b[i].l++;
            b[i].r++;
            b[i].id=i;
        }
        sort(a+1,a+n+1);
        sort(b+1,b+m+1);
        for(int i=1,j=1;i<=m;i++)
        {
            while(j<=n&&a[j].val<=b[i].val)
            {
                add(a[j].id,1);
                j++;
            }
            ans[b[i].id]=query(b[i].r)-query(b[i].l-1);
        }
        printf("Case %d:\n",cas++);
        for(int i=1;i<=m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

区间不同数的个数:

我们仅对a[i]的值的最后一个位置标记,就能通过查询(l,r)的和得区间不同数的个数

查询操作按r排序,a[i]不同排序,但是每次查询(l,r)时,我们限制a[i]的i小于等于r,最多插入n次

我们每次查询的时候,把(1,r)的a[i]都标记了,但是对于值相同的a[i],我们仅仅标记最后的位置,一种值最多产生一次贡献

对于值相同的a[i],我们只会计算一次产生的影响.我们查询的r是单调增的,插入的位置也是单调增的,保证正确性

#include
#define lowbit(x)  (x)&(-x)
#define ll long long
#define MAXN 400005
using namespace std;
ll a[MAXN],n,m;
unordered_map vis;
ll sum[MAXN];
ll ans[MAXN];

void add(ll x,ll c)
{
    for(ll i=x;i<=n;i+=lowbit(i))
        sum[i]+=c;
}

ll query(ll x)
{
    ll ans=0;
    for(ll i=x;i;i-=lowbit(i))
        ans+=sum[i];
    return ans;
}

struct node
{
    ll l,r,id;
    friend bool operator <(node a,node b)
    {
        return a.r

http://codeforces.com/contest/301/status/page/1?order=BY_JUDGED_DESC

区间非互质对数

题意:

#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 200010;

int a[MAXN], pos[MAXN], tree[MAXN];
int n, m;

int lowbit(int x) {
    return x & (-x);
}

int get_sum(int k) {
    int ans = 0;
    while(k > 0) {
        ans += tree[k];
        k -= lowbit(k);
    }
    return ans;
}

void modify(int k, int val) {
    while(k <= n) {
        tree[k] += val;
        k += lowbit(k);
    }
}

struct QUERY {
    int id, L, R;
    bool operator < (const QUERY &rhs) const {
        return L < rhs.L;
    }
} query[MAXN], query_t[MAXN];

int ans[MAXN];

int main()
{
    int g=0;
    for(int i=0;i<10;i++)
        g++;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
        pos[a[i]] = i;
    }
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d%d", &query[i].L, &query[i].R);
        query_t[i].L = query[i].R;
        query_t[i].R = query[i].L;
        query[i].id = query_t[i].id = i;
    }
    sort(query + 1, query + m + 1);
    sort(query_t + 1, query_t + m + 1);
    for(int i = 1, j = 1, k = 1; i <= n; ++i)
    {
        while(j <= m && query[j].L == i) {
            ans[query[j].id] -= get_sum(query[j].R) - get_sum(query[j].L - 1);
            ++j;
        }
        for(int p = a[i]; p <= n; p += a[i]) modify(pos[p], 1);
        while(k <= m && query_t[k].L == i) {
            ans[query_t[k].id] += get_sum(query_t[k].L) - get_sum(query_t[k].R - 1);
            ++k;
        }
    }
    for(int i = 1; i <= m; ++i)
        printf("%d\n", ans[i]);
}

区间(1,a)+(b,n)不同数的个数:

#include
#define MAXN 200005
using namespace std;
int a[MAXN],vis[MAXN],c[MAXN],gg[MAXN],ans[MAXN];
int mp[MAXN];
int n;
 
int lowbit(int x){
    return x&(-x);
}
 
struct node
{
    int l,r;
    int id;
    bool friend operator <(node a,node b)
    {
        return a.r0)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
 
int main()
{
    int m,la,ra;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i=1)
                g++;
        int j=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&la,&ra);
            if(ra-la<=1)
            {
                ans[i]=g;
                continue;
            }
            q[++j].l=la+1;
            q[j].r=ra-1;
            q[j].id=i;
        }
        sort(q+1,q+j+1);
        int k=j,tt=1;
        for(int i=1;i<=k;i++)
        {
            for(int j=tt;j<=q[i].r;j++)
            {
                if(vis[a[j]]==0)
                {
                    mp[a[j]]=j;//只标记一次
                    vis[a[j]]++;
                }
                else{
                    vis[a[j]]++;
                }
                if(vis[a[j]]==gg[a[j]])//如果出现次数==总次数,呢么就删完了
                {
                    add(mp[a[j]],1);//对标记点-1
                }
            }
            tt=q[i].r+1;
            ans[q[i].id]=g-(query(q[i].r)-query(q[i].l-1));
        }
        for(int i=1;i<=m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=6609

在尾部逐步插入n个元素,求插入第i个元素时,[1,i)内删去多少个元素,可使前缀和[1,i]不大于m

离散化数据,这里数据不多,不去重,方便操作

二分最多可以留下多少个,即得最少减去的数,用树状数组求和

ac:

#include
#define lowbit(x)  (x)&(-x)
#define MAXN 800005
#define ll long long
using namespace std;
ll n,m;
ll a[MAXN],b[MAXN];
ll sum[MAXN];
ll num[MAXN];
ll val[MAXN];
map hash1;

void add(ll x,ll c)
{
    for(ll i=x;i<=n;i+=lowbit(i))
    {
        sum[i]+=c,num[i]+=1;
    }
}

ll query(ll r)
{
    ll ans=0;
    for(ll i=r;i>0;i-=lowbit(i))
        ans+=sum[i];
    return ans;
}

ll query2(ll r)
{
    ll ans=0;
    for(ll i=r;i>0;i-=lowbit(i))
        ans+=num[i];
    return ans;
}

ll solve(ll i)//二分最多可以留下多少个,即得最少减去的数
{
    ll l=1,r=n;
    ll ans=0;
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        if(m-val[a[i]]>=query(mid))
        {
            l=mid+1;
            ans=max(ans,mid);
        }
        else{
            r=mid-1;
        }
    }
    ll ans2=query2(ans);
    return i-1-ans2;
}

int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        hash1.clear();
        memset(sum,0,sizeof(sum));
        memset(num,0,sizeof(num));
        scanf("%lld%lld",&n,&m);
        for(ll i=1;i<=n;i++)
            scanf("%lld",&a[i]),b[i]=a[i];
        sort(b+1,b+1+n);
        for(ll i=1;i<=n;i++)//这里离散化不去重
        {
            ll k=a[i];
            a[i]=lower_bound(b+1,b+1+n,a[i])-b+hash1[a[i]];//同值的不同数在不同的位置
            hash1[k]++;//累计数目
            val[a[i]]=k;//给位置标记权值
        }
        for(ll i=1;i<=n;i++)
        {
            printf("%lld ",solve(i));
            add(a[i],val[a[i]]);
        }
        printf("\n");
    }
    return 0;
}

https://nanti.jisuanke.com/t/A1253

题意:

给定2个串,A,B

q次询问

1.询问A的子串(l,r)中,出现多少次B

2.修改A串的第x个字母,修改为C

解析:

直接树状数组暴力处理

注意合法范围

如果修改x,则会对(max(1,x-m+1),min(n,x+m-1))区间产生影响

#include
#define lowbit(x) (x)&(-x)
#define MAXN 1000005
using namespace std;

char ss[MAXN];
char cc[11];
int n;
int sum[MAXN];

void init()
{
    memset(sum,0,sizeof(sum));
}

void add(int x,int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
        sum[i]+=v;
}

int query(int x)
{
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i))
        ans+=sum[i];
    return ans;
}

char qq[5];

int main()
{
    int t,q,l,r,x;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d",&q);
        scanf("%s",ss+1);
        scanf("%s",cc+1);
        n=strlen(ss+1);
        int m=strlen(cc+1);
        for(int i=1;i+m-1<=n;i++)
        {
            int sign=0;
            for(int j=1;j<=m;j++){
                if(ss[i+j-1]!=cc[j]){
                    sign=1;
                    break;
                }
            }
            if(sign==0)
                add(i,1);
        }
        while(q--)
        {
            scanf("%s",&qq);
            if(qq[0]=='Q'){
                scanf("%d %d",&l,&r);
                if(r-l+1>=m)
                    printf("%d\n",query(r-m+1)-query(l-1));
                else
                    printf("0\n");
            }
            else{
                scanf("%d %s",&x,&qq);
                ss[x]=qq[0];
                int st=max(1,x-m+1);
                int ed=min(n,x+m-1);
                for(int i=st;i<=ed;i++)
                {
                    int sign=0;
                    for(int j=1;j<=m;j++){
                        if(ss[i+j-1]!=cc[j]){
                            sign=1;
                            break;
                        }
                    }
                    int v=query(i)-query(i-1);
                    if(sign==0&&v==0)
                        add(i,1);
                    else if(sign==1&&v==1)
                        add(i,-1);
                }
            }
        }
        printf("\n");
    }
    return 0;
}

 

你可能感兴趣的:(数据结构)