zoukankan      html  css  js  c++  java
  • hdu 5648 DZY Loves Math 组合数+深搜(子集法)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5648

    题意:给定n,m(1<= n,m <= 15,000),求Σgcd(i|j,i&j);(1 <= i <= n,1<=j<=m);

    至多三组数据,至多两组数据max(n,m) > 2000.至多一组数据max(n,m) > 8000;

    很多题解是用递推打表,将数据压缩250倍,即[i][j]:代表[1...250*i][1...250*j],之后零头就直接暴力求解;这样时间复杂度为O(T*250*max(n,m));

    但是这并不完美。。代码长度接近5W b..如果卡代码长度的话,就呵呵了;

    事先说明代码是在BC看的AC代码,来源于NanoApe。特在此orz..

    思路:题解讲用枚举子集法,但是当时并没有看懂。。没有去细想在这个gcd里面是要确定什么,枚举什么。这就是深搜的思路了;

    原本看到gcd想的就是莫队反演。。莫队反演是容易求出同一个gcd里面的对数;但是这道题gcd里面并不是原始的a,b;而是a,b的位或位与运算以后的值;

    并且数据规模不大,(至少从组数及n,m最大值的限制上来看);

    现在我们就来枚举i|j,因为i&j能够在i|j枚举1,这就是题解上面的子集枚举的对象;

    下面分情况来看如何深搜;i表示两个数位或运算后的值;并且默认n >= m

    <1>当i<= m时,这时易知对于我们实际的值x,y一定是<=m的;直接在范围内枚举,即可dfs1();

    dfs1里面a 代表位或的值,b代表位与的值;容易看出b其实位元1是在a中选的;至于对ans的贡献是每一个a位元为1,b为0的位可以是两种,所以1<<(c[a]-c[b]);对于b为0的情况,会出现x = 0 || y = 0所以减去这二者;(d在dfs2中解释);

    注意并不是i <= n作为循环结束的标志,位或最大为2*n-1,这也是开始运算出mx的另一个用处;

    <2>当i > m时,没有a里面的每一个1,一定有一个数(x||y)的位元是1的,这就可以分成x,y,x&&y;对应三个if;
    对于这个位或值,如果前面有一个位1,原本x可以承担(即x += 1<<p 仍然<=n)但是并有没有取,而是被y承担了,这时之后任意位元为1的x均可以承担,这时对于x来说,进入dfs1就可以随便取a里面剩下的位了;这样就保证了所取得的值<=n;这就是为什么在判断是否能进入dfs1对后面的位元1进行组合数取的条件;即加的是(1<<(p+1))-1;

    <3>对于dfs2中的p == -1的情况,是否需要求解?答案是要的,因为能到p == -1表示里面的x,y是符合情况的;如果不符合情况,就会直接在前面就无法dfs2下去了;直接计算即可;至于d的含义,因为当a > m时,前面大的位元1一定是x承担的,并且这承担的在后面组合数枚举的时候,虽然没有在高位上枚举,但是c[a]还是记录了这高位的,d即表示这没有枚举的高位a比b多出来的个数;由于枚举到的位可能在m范围内,但是之后却没有枚举,所以出现b = x&y;

    很神奇~~~hhhhh

    1326ms 1824k 自己手写__builtin_popcount()用时1263ms 因为库函数是查表的,并没有真正计算;

    #include<bits/stdc++.h>
    using namespace std;
    #define rep0(i,l,r) for(int i = (l);i < (r);i++)
    #define rep1(i,l,r) for(int i = (l);i <= (r);i++)
    #define rep_0(i,r,l) for(int i = (r);i > (l);i--)
    #define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
    #define MS0(a) memset(a,0,sizeof(a))
    #define MS1(a) memset(a,-1,sizeof(a))
    #define MSi(a) memset(a,0x3f,sizeof(a))
    #define inf 0x3f3f3f3f
    #define lson l, m, rt << 1
    #define rson m+1, r, rt << 1|1
    typedef pair<int,int> PII;
    #define A first
    #define B second
    #define MK make_pair
    typedef __int64 ll;
    template<typename T>
    void read1(T &m)
    {
        T x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        m = x*f;
    }
    template<typename T>
    void read2(T &a,T &b){read1(a);read1(b);}
    template<typename T>
    void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);}
    template<typename T>
    void out(T a)
    {
        if(a>9) out(a/10);
        putchar(a%10+'0');
    }
    int T,kase = 1,i,j,k,n,m,x,y,a,b,d;
    #define N 1<<16
    int c[N];
    ll ans;
    inline ll gcd(ll a,ll b){return b == 0?a:gcd(b,a%b);}
    void dfs1(int p)
    {
        while(p >= 0) {
            if(!(a&(1<<p))){p--;continue;}
            b += (1<<p);
            dfs1(p-1);
            b -= (1<<p);
            p--;
        }
        if(b) ans += 1LL*gcd(a,b)*(1<<(c[a]-c[b]-d));
        else ans += 1LL*gcd(a,b)*((1<<(c[a]-c[b]-d))-(x==0?1:0)-(y==0?1:0));//x,y分别是各自的实际值;
    }
    void dfs2(int p)
    {
        if(p == -1){
            if(x == 0 || y == 0) return ;
            ans += gcd(x|y,x&y);
            return ;
        }
        while(p >= 0 && !(a&(1<<p))) p--;
        // **+(1<<p+1)-1是为了说明之后任意的枚举a中的位元1度不会超出n,m的范围
        if(x+(1<<p+1)-1 <= n && y+(1<<p+1)-1 <= m){ 
            b = x&y;
         dfs1(p);
    return ; } d++;//d表示x中1的个数比y中1的个数多d个 if(x+(1<<p) <= n) x += 1<<p,dfs2(p-1), x -= 1<<p;// x承担 if(y+(1<<p) <= m) y += 1<<p,dfs2(p-1), y -= 1<<p;// y承担 d--; if(x+(1<<p) <= n && y+(1<<p) <= m)// x,y均承担 x += 1<<p, y += 1<<p, dfs2(p-1), x -= 1<<p,y -= 1<<p; } int pop(unsigned x) //使用的是分治的思想 { x = x - ((x>>1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x = x + (x >> 8); x = x + (x >> 16); return x & 0x0000003F; } int main() { rep0(i,0,N) c[i] = pop(i);//__builtin_popcount read1(T); while(T--){ ans = 0; read2(n,m); if(n < m) swap(n,m); int mx = 0; while((1<<mx) <= n) mx++; rep1(i,1,(1<<mx)-1){ if(i <= m) a = i,b = 0,x = 0,dfs1(mx); else a = i,b = 0,x = 0,y = 0,d = 0,dfs2(mx); } out(ans); puts(""); } return 0; }
  • 相关阅读:
    位向量法、二进制法枚举子集
    jfinal 下载文件时重命名
    Java程序员开发参考资源
    (二)Hive1.2.2
    (一)Hive1.2.2
    YARN资源调度框架
    MapReduce并行编程模型
    HDFS 分布式文件系统
    Kafka
    观察者模式
  • 原文地址:https://www.cnblogs.com/hxer/p/5321520.html
Copyright © 2011-2022 走看看