zoukankan      html  css  js  c++  java
  • 【BZOJ4300】绝世好题(位运算水题)

    点此看题面

    大致题意: 给你一个序列(a),让你求出最长的一个子序列(b)满足(b_i&b_{i-1}!=0)

    位运算+(DP)

    考虑设(f_i)表示以第(i)个数为结尾所能得到的合法子序列的最长长度

    则一个数能从另一个数那里转移,当且仅当这两个数按位与的值不为(0)

    考虑按位与的值不为(0),实际意义就是二进制下存在至少一位上这两个数都是(1)

    那么,我们可以枚举两个位置,然后枚举二进制下一位判断是否可以转移。

    这样就可以轻松得出一个复杂度比暴力还劣的(O(n^2log a_i))的解法。

    实际上,在刚才的转移中其实有许多无意义转移。

    则我们需要知道,怎样的转移是有意义的。

    假设有(l,r(1le l<rle n))满足(a_l)(a_r)二进制下第(j)位上都为(1)

    则根据前面的转移,(r)必然可以由(l)转移,则(f_r)至少为(f_l+1),简而言之就是(f_r>f_l)

    也就是说,对于二进制下第(k)位为(1)的任何的位置(i)(i>r)),从(l)转移显然是无意义的。

    其实,对于每一个(j),只有从二进制下这一位为(1)的最靠右的位置转移才是有意义的。

    因此,我们设(g_j)表示二进制下第(j)位为(1)的最右位置,转移方程即为(转移时要满足(a_i)二进制下第(j)位为(1)):

    [f_i=max_{j=1}^{30}f_{g_j}+1 ]

    转移完之后,我们再次枚举每一个满足(a_i)二进制下第(j)位为(1)(j),然后更新(g_j=i)即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    using namespace std;
    int n,a[N+5],f[N+5],g[N+5];
    int main()
    {
    	RI i,j,ans=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);
    	for(i=1;i<=n;++i)
    	{
    		for(j=30;~j;--j) a[i]>>j&1&&Gmax(f[i],f[g[j]]);++f[i];//转移
    		for(j=30;~j;--j) a[i]>>j&1&&(g[j]=i);Gmax(ans,f[i]);//更新
    	}return printf("%d",ans),0;//输出答案
    }
    
  • 相关阅读:
    轻量级调试api接口 Jquery.PlayingAPI v 1.0
    js验证整数加保留小数点
    简单漂亮bootstrap grid列表分页 demo.
    polemo-logger 分析
    POSIX 标准的获取(pdf 格式)
    游戏开发利器(工具、资源)
    仅有 265 行的第一人称引擎
    介绍一款非常适合做微网站并且免费的CMS系统
    兔子无限繁殖问题——婓波那契数列
    近似计算
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4300.html
Copyright © 2011-2022 走看看