NOI模拟(5.7) HNOID1T1 寻宝游戏 (bzoj5285)

寻宝游戏

题目背景:

5.7 模拟 HNOI2018D1T1  

分析:思维分析

 

这道题感觉真的是很厉害的一道题,当时考场上因为的确不知道怎么做,于是就手玩了一下小数据,然后就发现,如果把|看成是0&看成是1的话,以第n个数之前的符号当成最高位,这样每一种填符号的方案就是一个n位二进制数,然后发现对于可行的符号方案,一定是一段连续的二进制数。并且这些可行解中最小的一个一定是将原来的n个数的相同位置提取出来,从n1形成的m个二进制数中的一个,而上界是这m个数中的一个减去1,然后又继续找发现了下边界的对应位置一定是结果串中为0的位置,上边界是为1的位置,然后下边界是0当中最大的,上边界是1当中最小的,就这么一步步找规律搞了出来,最后竟然还真的是对的······考虑系统证明一下,首先像我之前说的那样,每一种符号填法形成一个n位二进制数,然后n个原数提取每一位形成m个二进制数,最高位都是第n位,难么我们可以对于每一位分开考虑,显然,|0,或者是&1是没有用的,如果最终位为1,那么最后一个|1一定在最后一个&0之后,反之亦然,所以考虑这在二进制数上的体现,我们从高位向下比较运算符字符串,和形成的当前位字符串,如果两者当前位置相同,则不影响,如果第一个不同的位置,运算符为1,原位置串为0,那么最终这一位结果就是0,否则结果为1,如果两串相同,结果还是为0,那么对于最终位置是1的地方,运算符串显然要比当前位置串小,而如果最终位置是0的话,运算符串显然要大于等于当前位置串,所以我们只需要每一次找出最终位是0的位置串中最大的一个,已经最终位是1的位置串中最小的一个,后减前即为答案,注意判无解的情况即可。

 

Source:

/*
    created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <bitset>
 
inline char read() {
    static const int IN_LEN = 1024 * 1024;
    static char buf[IN_LEN], *s, *t;
    if (s == t) {
        t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
        if (s == t) return -1;
    }
    return *s++;
}
 
/*
template<class T>
inline void R(T &x) {
    static char c;
    static bool iosig;
    for (c = read(), iosig = false; !isdigit(c); c = read()) {
        if (c == -1) return ;
        if (c == '-') iosig = true; 
    }
    for (x = 0; isdigit(c); c = read()) 
        x = ((x << 2) + x << 1) + (c ^ '0');
    if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN];
char *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}


template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
    for (int i = 0; i <= 10; ++i) std::cout << obuf[i];
    std::cout << '\n';
	fwrite(obuf, 1, oh - obuf, stdout), oh = obuf;
}
 
// /*
template<class T>
inline void R(T &x) {
    static char c;
    static bool iosig;
    for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
        if (c == '-') iosig = true; 
    for (x = 0; isdigit(c); c = getchar()) 
        x = ((x << 2) + x << 1) + (c ^ '0');
    if (iosig) x = -x;
}
//*/

const int MAXN = 1000 + 9;
const int MAXM = 5000 + 9;
const int mod = 1000000000 + 7;

int n, m, q;
int sum[MAXM], rk[MAXM], temp[MAXM];
char s[MAXM];

int main() {
    //freopen("hunt.in", "r", stdin);
    //freopen("hunt.out", "w", stdout);
    R(n), R(m), R(q);
    int ret = 1;
    for (int i = 0; i <= m + 1; ++i) rk[i] = i;
    for (int i = 0; i < n; ++i, ret = ret * 2 % mod) {
        scanf("%s", s + 1);
        for (int j = 1; j <= m; ++j) 
            sum[j] = (sum[j] + (s[j] - '0') * ret) % mod;
        for (int j = 1, h = 0; j <= m; ++j)
            if (s[rk[j]] - '0') temp[++h] = rk[j];
        for (int j = m, t = m + 1; j >= 1; --j)
            if ((s[rk[j]] - '0') == 0) temp[--t] = rk[j];
        // for (int j = 1; j <= m; ++j) std::cout << temp[j] << " ";
        for (int j = 1; j <= m; ++j) rk[j] = temp[j];
    }
    // for (int i = 1; i <= m; ++i) std::cout << rk[i] << " ";
    // std::cout << '\n';
    sum[0] = ret, sum[m + 1] = 0;
    while (q--) {
        scanf("%s", s + 1);
        int p0 = m + 1, p1 = 0;
        for (int j = m; j >= 1; --j)
            if (s[rk[j]] - '0') {
                p1 = j;
                break ;
            } 
        for (int j = 1; j <= m; ++j)
            if ((s[rk[j]] - '0') == 0) {
                p0 = j;
                break ;
            }
        if (p0 <= p1) {
            puts("0");
            continue ;
        }
        std::cout << (sum[rk[p1]] - sum[rk[p0]] + mod) % mod << '\n';
    }
    return 0;
}

 

 

 

 

 

 

 

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值