zoukankan      html  css  js  c++  java
  • Nowcoder Playing Games ( FWT 优化 DP && 博弈论 && 线性基)

    题目链接

    题意 : 给出 N 个数、然后问你最多取出多少石子使得在 NIM 博弈中、后手必胜

    分析 : 

    Nim 博弈模型,后手必胜当且仅当各个堆的石子的数目的异或和为 0

    转化一下、变成最少取多少石子使得异或和为原来所有石子堆的异或和

    和背包DP思想很类似、可以考虑 DP

     dp[i][j] = 到第 i 个石子为止、使得异或和为 j 的最少取石子方案是多少

    但是如果这样子去构造 dp 转移显然是 O(n^2) 的

    如果你接触过 FWT 优化 DP 的题目的话、可能会想到如下的 DP 方程

    dp[i][j] = 取 i 个石子、是否能异或出 j 

    dp[i][j] == 0 代表没有 j 这个值、 != 0 则反之

    可能你会想为什么不直接用 bool 来作为 dp 类型

    因为 bool 不能做乘法啊、为什么要做乘法啊?

    因为要优化啊!可以考虑用 FWT 来优化这个 DP

    dp[i][j] = ∑ dp[i-1][K] * stone[L] ( L ^ K = j )

    注意这个 dp 的意义的第一维是石子个数、不是到第几个石子为止

    Stone[i] == 1 表示初始石堆的状态有 i 这个值、等于 0 则反之

    例如初始给出 1 2 4 这个石堆、则有

    Stone[1] = Stone[2] = Stone[4] = 1、Stone[3] = 0

    对于 FWT 做完后的 DP[i] == 0 代表没有 i 这个异或值、 != 0 代表有

    当 DP[原始所有石堆的异或和] != 0 的时候就代表找到了、此时答案等于 ( n - 你迭代的次数 )

    但是 DP 由于是做卷积、乘法相加会使得结果可能会很大造成溢出

    所以每次做完 FWT 要将 DP 值和 1 取个 min

    也就是用 1 来代表所有的非零状态、即存在这个数的状态

    还有记得初始 DP[0] = 1

    不过这个的第一维还是很大、此时你考虑二分

    显然这个是满足二分性质的、如果是取最多的石子异或和为 0 则不满足二分

    但是我们这里可以不考虑二分的做法

    实际石子的个数并不会超过 19 个

    因为 (1<<19) > 1e5(maxn)

    为什么呢、因为根据线性基的理论

    有 k 维度的线性基 (向量个数??) 最多只有 k 个

    ( 有没有大佬在评论具体解释一下为什么选不超过 19 个就行?)

    ( 我太弱了呀,看完线性基之后发现还是不太懂 ,只能强行解释?)

    那么在此题中、找超过 19 个的话那么必定是有线性相关的组合

    说实话、没以前没接触过线性基、所以对这个不是很了解

    总之当结论用??

    #include<bits/stdc++.h>
    #define LL long long
    #define ULL unsigned long long
    
    #define scl(i) scanf("%lld", &i)
    #define scll(i, j) scanf("%lld %lld", &i, &j)
    #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
    #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)
    
    #define scs(i) scanf("%s", i)
    #define sci(i) scanf("%d", &i)
    #define scd(i) scanf("%lf", &i)
    #define scIl(i) scanf("%I64d", &i)
    #define scii(i, j) scanf("%d %d", &i, &j)
    #define scdd(i, j) scanf("%lf %lf", &i, &j)
    #define scIll(i, j) scanf("%I64d %I64d", &i, &j)
    #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k)
    #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k)
    #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k)
    #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l)
    #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l)
    #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l)
    
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    #define lowbit(i) (i & (-i))
    #define mem(i, j) memset(i, j, sizeof(i))
    
    #define fir first
    #define sec second
    #define VI vector<int>
    #define ins(i) insert(i)
    #define pb(i) push_back(i)
    #define pii pair<int, int>
    #define VL vector<long long>
    #define mk(i, j) make_pair(i, j)
    #define all(i) i.begin(), i.end()
    #define pll pair<long long, long long>
    
    #define _TIME 0
    #define _INPUT 0
    #define _OUTPUT 0
    clock_t START, END;
    void __stTIME();
    void __enTIME();
    void __IOPUT();
    using namespace std;
    const int maxn = 1e6 + 10;
    
    
    void FWT(LL f[], int n, int op) {
        int mx = 0;
        while((1LL<<mx) < n) mx++;
        for (int i = 1; i <= mx; ++i) {
            int m = (1 << i), len = m >> 1;
            for (int r = 0; r < n; r += m) {
                int t1 = r, t2 = r + len;
                for (int j = 0; j < len; ++j, ++t1, ++t2) {
                    LL x1 = f[t1], x2 = f[t2];
                    if (op == 1) {   //xor
                        f[t1] = x1 + x2;
                        f[t2] = x1 - x2;
                        //if(f[t1] >= mod) f[t1] -= mod;
                        //if(f[t2] < 0) f[t2] += mod;
                    }
                    if (op == 2) {   //and
                        f[t1] = x1 + x2;
                        f[t2] = x2;
                        //if(f[t1] >= mod) f[t1] -= mod;
                    }
                    if (op == 3) {   //or
                        f[t1] = x1;
                        f[t2] = x2 + x1;
                        //if(f[t2] >= mod) f[t2] -= mod;
                    }
                }
            }
        }
    }
    
    void IFWT(LL f[], int n, int op) {
        int mx = 0;
        while((1LL<<mx) < n) mx++;
        for (int i = mx; i >= 1; --i) {
            int m = (1 << i), len = m >> 1;
            for (int r = 0; r < n; r += m) {
                int t1 = r, t2 = r + len;
                for (int j = 0; j < len; ++j, ++t1, ++t2) {
                    LL x1 = f[t1], x2 = f[t2];
                    if (op == 1) {   //xor
                        f[t1] = (x1 + x2) / 2;
                        f[t2] = (x1 - x2) / 2;
    //                    f[t1] = (x1 + x2) * inv2;
    //                    f[t2] = (x1 - x2) * inv2;
    //                    if(f[t1] >= mod) f[t1] %= mod;
    //                    if(f[t2] >= mod) f[t2] %= mod;
    //                    if(f[t2] < 0) f[t2] = f[t2] % mod + mod;
                    }
                    if (op == 2) {   //and
                        f[t1] = x1 - x2;
                        f[t2] = x2;
                        //if(f[t1] < 0) f[t1] += mod;
                    }
                    if (op == 3) {   //or
                        f[t1] = x1;
                        f[t2] = x2 - x1;
                        //if(f[t2] < 0) f[t2] += mod;
                    }
                }
            }
        }
    }
    
    LL arr[maxn], dp[maxn];
    int Tot_xor_Sum = 0;
    int Len = 0;
    int main(void){__stTIME();__IOPUT();
    
        int n;
    
        sci(n);
    
        for(int i=0; i<n; i++){
            int num; sci(num);
            arr[num]++;
            Len = max(Len, num);
            Tot_xor_Sum ^= num;
        }
    
        int Bit = 0;
        while((1<<Bit) <= Len) Bit++;
    
        Len = (1<<Bit);
    
        dp[0] = 1LL;
    
        FWT(arr, Len, 1);
    
        int ans = n;
        while(!dp[Tot_xor_Sum]){
            ans--;
    
            FWT(dp, Len, 1);
    
            for(int i=0; i<Len; i++) dp[i] = (dp[i] * arr[i]);///其实就是不断做FWT、相当于 arr 数组的 n - ans 次幂
    
            IFWT(dp, Len, 1);
    
            for(int i=0; i<Len; i++) dp[i] = min(dp[i], 1LL);///FWT后的DP值可能会溢出、所以取个min
        }
    
        printf("%d
    ", ans);
    
    
    
    
    
    
    
    
    
    
    __enTIME();return 0;}
    
    
    void __stTIME()
    {
        #if _TIME
            START = clock();
        #endif
    }
    
    void __enTIME()
    {
        #if _TIME
            END = clock();
            cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl;
        #endif
    }
    
    void __IOPUT()
    {
        #if _INPUT
            freopen("in.txt", "r", stdin);
        #endif
        #if _OUTPUT
            freopen("out.txt", "w", stdout);
        #endif
    }
    View Code
  • 相关阅读:
    CF 461B Appleman and Tree
    POJ 1821 Fence
    NOIP 2012 开车旅行
    CF 494B Obsessive String
    BZOJ2337 XOR和路径
    CF 24D Broken robot
    POJ 1952 BUY LOW, BUY LOWER
    SPOJ NAPTIME Naptime
    POJ 3585
    CF 453B Little Pony and Harmony Chest
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9517983.html
Copyright © 2011-2022 走看看