zoukankan      html  css  js  c++  java
  • 【HNOI2018寻宝游戏】

    寻宝游戏

    题意:

    给定(n)个长度为(m)(01)

    现在允许你在对(0)依次“按位与”或者“按位或”上这些(01)

    (q)组询问,每次询问有多少种方法填入“按位与”或者“按位或”使得最终结果为询问的(01)

    (Sol:)

    (1.)

    [0~|~1~=~1~,quad 1~|~1~=~1 ]

    [0~&~0=~0,quad 1~&~0~=~0 ]

    于是有对于一个(1)如果其前面填入的为(~|~),那么无论如何答案都是(1)

    对于一个(0)如果其前面填入的为(&),那么无论如何答案都是(0)

    (2.)

    [1~|~0~=~1~,quad 0~|~0~=~0 ]

    [1~&~1=~1,quad 0~&~1~=~0 ]

    于是有对于一个(1)如果其前面为(&)那么答案不会改变

    对于一个(0)如果其前面为(~|~)那么答案也不会改变

    (3.)

    我们先考虑最基础的问题,假设所有的(0/1)串都只有(1)

    我们从后往前考虑所有的(0/1),可以将其变成一个序列

    由于两两之间也有一个操作,于是把操作单独提出来也是一个序列

    规定它为操作序列,我们尝试把操作序列转成一个(0/1)序列

    (~|~)(0)(&)(1)

    于是对于一个(0/1)序列(10010)等,其运算结果为(1)当且仅当其(>)操作序列,否则其为(0)

    这是因为对于操作序列,如果偏高位与(0/1)序列全部相等,则为情况(2)运算结果不变,否则如果存在一个(1)且操作序列对应位为(0)则为情况(1),运算结果为(1)

    于是对于一位的情况,我们成功将这道题转化成了一道比大小的题目了(qwq)

    对于每个(0/1)串若其有(m)位我们就对其每一位单独考虑

    若结果要求当且位为(1)则表明操作序列(<)当且位,否则表示操作序列(ge)当前位

    于是我们将所有的(0/1)串提前排个序,问题就只需要取(0)中最大值(x)(1)中最小值(y)(y-x)即方案数

    一点细节

    注意到我们最后要反向,还要对每一位单独处理

    于是好像有我们读入的就是待排序的最低位,次低位...最高位

    于是好像可以边读入边基排...

    复杂度(O(nm+qm))

    #include<bits/stdc++.h>
    using namespace std;
    #define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
    #define drep( i, s, t ) for( register int i = t; i >= s; -- i )
    #define int long long
    #define re register
    int read() {
    	char cc = getchar(); int cn = 0, flus = 1;
    	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    	return cn * flus;
    }
    const int P = 1000000007 ; 
    const int N = 1000 + 5 ;
    const int M = 5000 + 5 ; 
    int n, m, q, lk[M], rk[M], b[M][N], a[M], Ans[M] ; 
    char s[M] ;
    int Get( int x, int y ) {
    	return ( Ans[y] - Ans[x] + P ) % P ; 
    }
    signed main()
    {
    	n = read(), m = read(), q = read() ; 
    	rep( i, 1, m ) rk[i] = i ; 
    	rep( i, 1, n ) {
    		scanf("%s", s + 1 ) ; int rs = 0 ; 
    		rep( j, 1, m ) a[j] = s[j] - '0', b[j][i] = a[j] ; 
    		rep( j, 1, m ) if( a[rk[j]] == 0 ) lk[++ rs] = rk[j] ;
    		rep( j, 1, m ) if( a[rk[j]] == 1 ) lk[++ rs] = rk[j] ;
    		rep( j, 1, m ) rk[j] = lk[j] ; 
    	}
    	rep( j, 1, m ) drep( i, 1, n ) Ans[j] = ( Ans[j] * 2ll + b[j][i] ) % P ;
    	rep( j, 1, n ) Ans[m + 1] = ( Ans[m + 1] * 2ll + 1 ) % P ; 
    	rk[m + 1] = m + 1 ; Ans[m + 1] += 1 ; 
    	while( q -- ) {
    		scanf("%s", s + 1 ) ; int Lk = 0, Rk = m + 1 ; 
    		rep( j, 1, m ) if( s[rk[j]] == '1' ) { Rk = j ; break ; }
    		drep( j, 1, m ) if( s[rk[j]] == '0' ) { Lk = j ; break ; } 
    		printf("%lld
    ", ( Rk < Lk ) ? 0 : Get( rk[Lk], rk[Rk] ) ) ;
    	} 
    	return 0;
    }
    
  • 相关阅读:
    hibernate常用查询语句动态生成类(包括条件和无条件查询)
    Eclipse常用插件更新下载地址列表
    IT相关网站列表
    /etc/目录下的passwd文件内容详解
    关于jfreechart创建web报表图片的流程初解
    博客地址列表
    java编码转换的详细过程 (转)
    偿债
    汽车变速器(自动挡)英文缩写
    Firefox 快捷键列表
  • 原文地址:https://www.cnblogs.com/Soulist/p/11637309.html
Copyright © 2011-2022 走看看