三元组[01 Trie计数]

也许更好的阅读体验

\(\mathcal{Description}\)

在这里插入图片描述

\(\mathcal{Solution}\)

有两种方法都可以拿到满分

\(Solution\ 1\)

考虑枚举\(y\)
建两个\(01Trie\),要支持删除操作
一颗\(Trie\)维护\(y\)左边的信息
一颗\(Trie\)维护\(y\)右边的信息
在枚举\(y\)的时候左边的添加,右边的删除,可做到\(log\)维护,建树是\(nlog\)
暴力的想法是两个\(Trie\)一起跑,枚举在哪一位开始不一样,前面的情况也都枚举,这样的情况最坏是\(2\)的指数级别,可以拿到\(50\)
考虑优化这个过程
因为每次只会删去一条链,所以考虑这条链被删去所带来的影响
\(f[i][0/1]\)表示到第\(i\)位开始不一样的答案
每次删除时只少了一条链,所以把这条链的情况减去
增加时加上新的情况即可

\(Solution\ 2\)

这种方法相对来说代码量要少很多
考虑枚举\(z\)
用一颗\(Tire\)表示前面的信息
仍然考虑枚举在哪一位开始不一样\((d)\)
\(s[i][0/1]\)表示前面所有在第\(i\)位为\(0/1\)的串的总数
\(sum[i]\)表示\(Tire\)\(i\)号节点有多少个不合法的\((x,y)\)\((y<x)\)
\(cnt[i]\)表示\(Tire\)上经过\(i\)的个数
由于枚举的是\(z\),我们对\(y\)的要求只有 枚举到\(d\)时,其在\(d\)\(0\)还是\(1\)
假设枚举到的\(z\)的值在\(d\)位是\(c\)
答案就是cnt[原本串中和\(z\)\(d\)前面的位相同]\(\times s[d][!c]-\)不合法的\((x,y)\)对数
具体可看代码

\(\mathcal{Code}\)

\(Solution\ 2\)
因为这种好实现些,所以就写的这种

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月23日 星期一 19时48分07秒
*******************************/
#include <cstdio>
#include <fstream>
#include <cstring>
#define ll long long
#define reset(x) memset(x,0,sizeof(x))
using namespace std;
const int maxn = 3000006;
const int lim = 29;
//{{{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;
//}}}
int T,n,v,tot;
int ch[maxn][2],s[lim+1][2];
ll sum[maxn],cnt[maxn];
void insert (int rt,int v,int d)
{
    if (d<0)    return;
    int c=(v>>d)&1;
    if (!ch[rt][c]) ch[rt][c]=++tot;
    rt=ch[rt][c];
    ++cnt[rt],sum[rt]+=++s[d][c];
    insert(rt,v,d-1);
}
ll query (int rt,int v,int d)
{
    if (d<0)    return 0;
    int c=(v>>d)&1,p=ch[rt][c^1];
    return cnt[p]*s[d][c^1]-sum[p]+query(ch[rt][c],v,d-1);
}
int main()
{
    cin>>T;
    while (T--){
        cin>>n;
        ll ans=tot=0;
        reset(sum),reset(s),reset(ch),reset(cnt);
        for (int i=1;i<=n;++i){
            cin>>v;
            insert(0,v,lim);
            ans+=query(0,v,lim);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值