UOJ2 - [NOI2014] 起床困难综合症

2017/01/17

居然已经鶸到去补之前写的题解。

题目

21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争。通过研究相关文献,他找到了该病的发病原因:在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。正是由于 drd 的活动,起床困难综合症愈演愈烈,以惊人的速度在世界上传播。为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。
历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。具体说来,drd 的防御战线由 n 扇防御门组成。每扇防御门包括一个运算 op 和一个参数 t,其中运算一定是 OR,XOR,AND 中的一种,参数则一定为非负整数。如果还未通过防御门时攻击力为 x,则其通过这扇防御门后攻击力将变为 x op t。最终 drd 受到的伤害为对方初始攻击力 x 依次经过所有 n 扇防御门后转变得到的攻击力。
由于 atm 水平有限,他的初始攻击力只能为 0 到 m 之间的一个整数(即他的初始攻击力只能在 0,1,…,m 中任选,但在通过防御门之后的攻击力不受 m 的限制)。为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。

解法

考虑到位运算各位独立,直接对着这个门所构成的函数求一下 全0 和 全1 两个二进制向量。
然后从高位向低位贪心就好,有点像数位 DP,先贪高位防止贪出来的数大于 m。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>
#include <limits>
using namespace std;
typedef long long LL;
const int MAX_N = 100000 + 100;
enum   OPT{AND, OR, XOR};
struct KT {OPT op;LL opc;} F[MAX_N];
LL N, M;

LL eval(LL x) {
    LL ans = x;
    for(int i=0;i<N;i++) {
        switch(F[i].op) {
            case AND: ans &= F[i].opc; break;
            case OR : ans |= F[i].opc; break;
            case XOR: ans ^= F[i].opc; break;
        }
    }
    return ans;
}

int main() {
    cin >> N >> M;

    string tt;
    for(int i=0;i<N;i++) {
        cin >> tt >> F[i].opc;
        switch(tt[0]) {
            case 'A': F[i].op = AND; break;
            case 'O': F[i].op = OR; break;
            case 'X': F[i].op = XOR; break;
        }
    }

    LL li = 1;
    while (li <= M) li <<= 1; // High bit
    LL ans = 0;
    LL zer = eval(0);
    LL ons = eval(numeric_limits<LL>::max());
    while(li >>= 1) {
        int ee = ons & li;
        if (!(zer & li) && ans + ee <= M && ee) ans += ee;
    }

    cout << eval(ans);
    return 0;
}