Tiny Counting

Posted morning-glory

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tiny Counting相关的知识,希望对你有一定的参考价值。

技术图片

样例一

  • 输入
    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;
}

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

以上是关于Tiny Counting的主要内容,如果未能解决你的问题,请参考以下文章

tiny os 常用代码

Tiny6410之重定位代码到SRAM+6096

C++卷积神经网络实例:tiny_cnn代码详解——层间继承关系

寥寥几行代码,却改变了世界!

UVaOJ1225 Digit Counting AC代码示例

Tiny语言编译器简单介绍