【GDOI2020模拟01.16】划愤(nim积+行列式)

https://gmoj.net/senior/#contest/show/2989/1

先考虑n=2时怎么做,打表找规律找了半天找不出来。

赛后才知道这是nim积。

定义\(x?y\)为\(sg(x,y)\)。

有一坨性质:
\(x,y<2^{2^k},x?y<2^{2^k}\)

\(2^{2^k}?2^{2^k}={3 \over 2}2^{2^k}\)

可以把?看做乘法,\(⊕\)(异或)看做加法,所以还有分配律。

求\(x?y(x>y)\),设\(k\)为最大的\(k\)满足\(2^{2^k}<=x\),设\(M=2^{2^k}\)

\(x=s*M+t=s*M⊕t,y=p*M+q=p*M⊕q\)

则\(x?y=spMM+sMq+tpM+tq\)

\(=M(sp+sq+tp)+tq+(M/2?sp)\)

字母间省略了\(?\)号,\(+\)号即\(⊕\)。

递归求出\(sp,sq,tp,tq,M/2sp\)即可。

时间复杂度:\(O(5^{log~log~V})\)。

预处理\((0-255,0-255)\)会快许多。

题目中重定义加法和乘法,求行列式即可。

注意行列式要逆元,\(v^{-1}=v^{2^{2^k}-2}\)(满足费马小定理)。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

#define ul unsigned long long

const ul maxM = (ul) 1 << 32;
const ul a2_63 = (ul) 1 << 63;

const int C = 256;

ul b[C][C];

ul calc2(ul x, ul y) {
    if(!x || !y) return 0;
    if(x == 1) return y;
    if(y == 1) return x;
    if(x < y) return calc2(y, x);
    ul M = 2; while(M < maxM && M * M <= x) M *= M;
    ul p = x / M, q = x % M;
    ul s = y / M, t = y % M;
    ul c1 = calc2(p, s), c2 = calc2(p, t) ^ calc2(q, s), c3 = calc2(q, t);
    return M * (c1 ^ c2) ^ c3 ^ calc2(M / 2, c1);
}

ul calc(ul x, ul y) {
    if(x < C && y < C) return b[x][y];
    if(x < y) swap(x, y);
    ul M = 2; int k = 1;
    while(M < maxM && M * M <= x) M *= M, k *= 2;
    ul p = x >> k, q = x & (M - 1);
    ul s = y >> k, t = y & (M - 1);
    ul c1 = calc(p, s);
    return ((c1 ^ calc(p, t) ^ calc(q, s)) << k) ^ calc(q, t) ^ calc(M / 2, c1);
}

ul ksm(ul x, ul y) {
    ul s = 1;
    for(; y; y /= 2, x = calc(x, x))
        if(y & 1) s = calc(s, x);
    return s;
}

ul qni(ul x) {
    if(x >= maxM) return ksm(x, (a2_63 - 1) * 2);
    ul M = 2; while(M <= x) M *= M;
    return ksm(x, M - 2);
}

const int N = 155;

int n;
ul a[N][N];

int main() {
    freopen("partition.in", "r", stdin);
    freopen("partition.out", "w", stdout);
    ff(i, 0, C) ff(j, 0, C) b[i][j] = calc2(i, j);
    scanf("%d", &n);
    fo(i, 1, n) fo(j, 1, n) {
        scanf("%llu", &a[i][j]);
    }
    int ye = 1;
    fo(i, 1, n) {
        int u = -1;
        fo(j, i, n) if(a[j][i])
            u = j;
        if(u == -1) { ye = 0; break;}
        fo(j, i, n) swap(a[u][j], a[i][j]);
        ll v = qni(a[i][i]);
        fo(j, i, n) a[i][j] = calc(a[i][j], v);
        fo(j, i + 1, n) if(a[j][i]) {
            v = a[j][i];
            fo(k, i, n) a[j][k] ^= calc(a[i][k], v);
        }
    }
    pp("%s\n", ye ? "xiaoDyingle" : "xiaoDwandanle");
}

原文地址:https://www.cnblogs.com/coldchair/p/12203219.html

时间: 2024-10-09 22:12:09

【GDOI2020模拟01.16】划愤(nim积+行列式)的相关文章

【GDOI2020模拟01.16】树上的鼠 (博弈+长链剖分优化dp)

https://gmoj.net/senior/#contest/show/2989/2 思考什么时候先手会赢. 一开始双方都不会希望走到直径的端点上,因为那样对方就可以走直径而使自己输掉. 删掉直径的端点,考虑剩下的树的子问题. 如果又走到端点去了,对面就走到另外一个端点,那我就走到下一层的直径端点去了. 所以大家一直都不想走到直径端点. 一直删,如果最后只剩1一个点,说明先手必败,否则先手必胜. 如果是一条链,就是链的两边的长度不等先手就必胜. 如果是一棵树,考虑随便找一条直径,每次删去它的

6438. 【GDOI2020模拟01.16】树上的鼠

题目 由于时间过于久远,而且题面本身也很清晰,所以就懒得另外叙述题目大意了(还有思考历程). 正解 先考虑一条链的情况(长度为奇数,这里的长度是指点的数量): 如果根在中点,先手无论移到哪里,后手都可以移到它的对称点去. 此时先手必败: 如果根不在中点,先手只要一开始移到中点,先手就赢了. 若长度为偶数,就将中间的两个点都看成中点. 先手第一步先移到离根比较远的那个中点上,以后就用一样的策略,每次到达对方的对称点.所以偶数时先手必胜. 然后这就可以推广到一棵树的情况. 可以发现先手必败的情况当且

6442. 【GDOI2020模拟01.18】钩子

题目描述 Description Input Output Sample Input Sample Input1 3 1000000007 Sample Input2 4 1000000007 Sample Output Sample Output1 0 1 0 500000004 0 500000004 500000004 0 500000004 Sample Output2 0 500000004 500000004 0 333333336 166666668 166666668 33333

【GDOI2020模拟01.17】小 ω 玩游戏 (容斥+EGF)

小 ω 正在玩一个游戏. 小 ω 有一个 n 行 m 列的网格,初始每个方格中都有数字 0.她需要执行 q 次操作,每次操作可以选择其中一个方格 (x, y),然后先将第 x 行的数全部 +1,接着将第 y 列的数全部 +1. 小 ω 想知道有多少种执行操作的方式能使最后的网格中有不超过 k 个奇数. 两种方式不同当且仅当存在某一步中选择的方格坐标不同. \(1<=n,m<=2e5,q<=10^{18}\) 考虑行列分开,对行,算出\(f(x)\)表示恰好x行奇数的方案数,对列同理,算出

6439. 【GDOI2020模拟01.17】小 ω 数排列

题目描述 Description Input Output Sample Input Sample Input1 4 10 3 6 2 9 Sample Input2 8 35 3 7 1 5 10 2 11 6 Sample Output Sample Output1 6 [样例 1 解释] 共有 6 个排列符合条件,它们是 (1, 3, 2, 4),(2, 4, 1, 3),(3, 1, 2, 4),(3, 1, 4, 2),(4, 2, 1, 3),(4, 2, 3, 1). Sample

6441. 【GDOI2020模拟01.17】小 ω 维护序列

Description Input Output 输出到标准输出流中. 若干行,对于每个操作 1 和操作 5,输出一个数表示答案. Sample Input Sample Input1 5 8 1 2 3 2 1 1 1 3 5 1 5 2 2 4 1 2 4 3 3 4 0 5 1 1 2 1 1 5 Sample Input2 10 15 5 4 3 5 4 1 5 4 3 1 2 8 580347 4 6 503576 1 2 5 5 8 11 1 2 6 4 7 565239 3 6 3

6445. 【GDOI2020模拟01.19】String

题目 正解 一听到正解是NTT,我心态崩了. 我特么知道是NTT都不知道该怎么做!哪里像个卷积了? 好吧,是我孤陋寡闻-- 设两个字符串分别为\(A\)和\(B\) 可以考虑试着计算出\(A\)每个子串和\(B\)的相似度(就是位置相同.字母相同的个数),直接统计就完事了. 看到字符集这么小,就可以对于每个字母分开考虑. 假如\(A_i=B_j\),那么以\(A_{i-j+1}\)开头的子串就有\(1\)的贡献. 这样一来,看起来就很像是个卷积了. 搞完之后将贡献加起来,统计即可. 总结 还是没

6444. 【GDOI2020模拟01.18】树高

题目 正解 这题也不是给人写出来的-- 先不要考虑操作二. 有一种比较新奇的思路:将点的颜色挂在边上,然后搞出个边的连通块. 这些连通块的维护是很舒服的,直接上数据结构即可. 考虑边的连通块和点的连通块的关系. 假如有\(x\)和\(y\)和\(z\)三个点相连,\(x\)为\(y\)父亲,\(y\)为\(z\)父亲. \((x,y)\)和\((y,z)\)的颜色相同,意味着\(y\)和\(z\)的颜色相同. 推广一下,我们可以发现,对于一个边连通块而言,除了根节点(需要特判是不是整棵树的根节点

6447. 【GDOI2020模拟01.19】sort

题目 正解 这题一看也是不能写的题目-- 用个平衡树来维护. 平衡树维护的是一个\(Trie\)的序列.每个\(Trie\)表示这段区间中所有数都会排序一遍. 进行各种操作时,首先会将至多两个节点的\(Trie\)分裂.分裂\(Trie\)会新增\(O(\lg n)\)个节点. 然后将整段区间旋到一棵子树内,然后打标记.平衡树和\(Trie\)上都要打标记. 排序是时候将若干个\(Trie\)合并成一个. 由于这些\(Trie\)是带标记的,所以要将标记下传.\(Trie\)树上标记下传时,如果