• 三元组[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)前面的位相同]( imes 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
    ",ans);
    	}
    	return 0;
    }
    
    

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

  • 相关阅读:
    centos安装vim
    thrift学习之二----学习资料积累
    thrift学习之一-------介绍
    组合模式
    一致性哈希算法(consistent hashing)
    php配置php-fpm启动参数及配置详解
    error while loading shared libraries的解決方法
    数据结构之二叉树
    768、最多能完成排序的块(贪心算法)
    VS code 配置C++编译环境
  • 原文地址:https://www.cnblogs.com/Morning-Glory/p/11574788.html
Copyright © 2020-2023  润新知