Tiny Counting

本文深入探讨了一个关于求解特定序列对数量的算法问题,通过详细的步骤分解和数学公式推导,阐述了如何高效地计算顺序对和逆序对的数量,并提供了一段C++代码实现,旨在帮助读者理解和掌握此类问题的解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

也许更好的阅读体验
样例好像并没有出现

样例一
  • 输入
    4
    1 4 3 2
  • 输出
    3
样例二
  • 输入
    5
    9 1 0 0 5
  • 输出
    8

题解

这是本人自己想了2个半小时才想出来的方法,稍稍有点复杂但是很好理解
题目的意思就是给定一个数组,求有多少个数字不同的顺序对和逆序对(Sa<Sb,Sc>Sd)
那么总方案数应该就是 顺序对数×与之对应合法的逆序对数
当然这些是不可以直接算出来的

sx[a] 表示a与后面的数组成的顺序对个数(a,x)
nx[a] 表示a与后面的树组成的逆序对个数
asnx[a] 表示a作为前面多少个数的逆序对(x,a)
sxc[a] 表示a的所有顺序对的作为逆序对个数的和
sxnx[a] 表示a的顺序对的逆序对个数
sum_nx 所有的逆序对总数
暂时没理解没关系,后面会解释清楚

考虑先枚举一个a,那么我们要做的就是找到剩下的b,c,d了

现在有这样的一个公式\(sx[a]*sum_nx\)即将a组成的顺序对与所有的逆序对组合,显然这是错误的,我们考虑将其变得正确

考虑最简单的,先去除a=c的情况
对于这种情况,因为我们把a的顺序对与所有逆序对组合了,现在不能要a开头的逆序对,我们只需 把a的逆序对个数减去 即可
\(sx[a]*(sum_nx-nx[a])\)

再考虑去除a=d的情况
对于这种情况,我们只需 把a作为前面的逆序对的个数减去 即可
\(sx[a]*(sum_nx-nx[a]-asnx[a])\)

再去除b=c的情况
实际上,我们也只需要 把b的逆序对个数减去 即可
\(sx[a]*(sum_nx-nx[a]-asnx[a]-nx[b])\)
但是上面的b对于不同a的顺序对这样我们又得枚举b,复杂度就降低很多
我们把上面的式子好好想一下,所有的b都会被算且只会被算一次那么其实我们是可以用树状数组提前维护好的
即维护a的所有顺序对的逆序对的个数
式子就变成\(sx[a]*(sum_nx-nx[a]-asnx[a])-sxnx[a])\)

最后还剩b=d的情况
对于a所有的顺序对,都有机会作为b,这时以它为d的都不行了
所以要减去顺序对的作为逆序对的个数即减去sxc[a]
\(sx[a]*(sum_nx-nx[a]-asnx[a])-sxnx[a]-sxc[a]\)

所有的情况都讨论完了,最终的答案就是枚举所有的a
\(ans+=sx[a]*(sum_nx-nx[a]-asnx[a])-sxnx[a]-sxc[a]\)

代码
由于不开long long博主只得了70分,所以后来就加了一堆long long,复杂度是肯定没有问题的

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年05月11日 星期六 08时10分14秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
#define rint register int
#define num(x) s[x].num
#define id(x) s[x].id
#define sx(x) s[x].sx
#define nx(x) s[x].nx
#define asnx(x) s[x].asnx
#define sxc(x) s[x].sxc
#define sxnx(x) s[x].sxnx
#define nxsx(x) s[x].nxsx
#define ll long long
using namespace std;
const int maxn = 100005;
struct Number{
    ll id,num,sx,nx,asnx,sxc,sxnx;
}s[maxn];
int n,cnt;
ll ans,sum_nx;
ll c[5][maxn];//0 逆序   1 顺序     2 作为逆序       3 顺序的作为逆序      4 顺序的逆序
//{{{cin 读入优化
struct IO{
    template<typename T>
    IO & operator>>(T&res){
        res=0;
        bool flag=false;
        char ch;
        while((ch=getchar())>'9'||ch<'0')    flag|=ch=='-';
        while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
        if (flag)    res=~res+1;
        return *this;
    }
}cin;
//}}}
//排序用于离散化
inline bool comp (Number x,Number y) { return x.id<y.id; }//按输入顺序排序
inline bool com (Number x,Number y){ return x.num<y.num; }//按数值大小排序
inline int lowbit (int x){ return x & -x; }
//{{{insert(l,r,v,x)树状数组的插入
void insert (int l,int r,ll v,int x) {
        for (ll i=l;i<=n;i+=lowbit(i))      c[x][i]+=v; 
    for (ll i=r+1;i<=n;i+=lowbit(i))    c[x][i]-=v;
}
//}}}
//{{{query(l,x)树状数组的查询
ll query (int l,int x) {
        ll res=0;
    for (ll i=l;i>=1;i-=lowbit(i)) res+=c[x][i];
    return res;
}
//}}}
//{{{pre 将数据离散化
void pre ()
{
    sort(s+1,s+n+1,com);
    for (rint i=1;i<=n;++i){
        bool flag=num(i)==num(i+1);
        num(i)=++cnt;
        cnt-=flag;
    }
    sort(s+1,s+n+1,comp);
}
//}}}
//{{{deal
void deal ()//维护出每个位置的,顺序,逆序等等
{
//0 逆序   1 顺序     2 作为逆序       3 顺序的作为逆序      4 顺序的逆序
    for (rint i=1;i<=n;++i){
        insert(1,num(i)-1,1,2);
        asnx(i)=query(num(i),2);
    }
    for (rint i=n;i>=1;--i){
        nx(i)=query(num(i),0);//逆序
        sx(i)=query(num(i),1);//顺序
        sxc(i)=query(num(i),3);//顺序的作为逆序

        insert(num(i)+1,n,1,0);//逆序
        insert(1,num(i)-1,1,1);//顺序
        insert(1,num(i)-1,asnx(i),3);//顺序的作为逆序

        sum_nx+=nx(i);//总逆序
    }
    for (rint i=n;i>=1;--i){
        sxnx(i)=query(num(i),4);//顺序的逆序个数
        insert(1,num(i)-1,nx(i),4);//同上
    }
}
//}}}
int main()
{
    cin>>n;
    for (rint i=1;i<=n;++i) 
        cin>>num(i),id(i)=i;
    
    pre();//离散化
    deal();//预处理
    
    for (rint a=1;a<=n;++a)
        ans+=1ll*sx(a)*(sum_nx-nx(a)-asnx(a))-sxnx(a)-sxc(a);
        
    printf("%lld\n",ans);
    return 0;
}

如以上没有看懂请指出问题,博主会加以修改,谢谢

转载于:https://www.cnblogs.com/Morning-Glory/p/10851953.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值