• [Contest on 2022.5.31] 在 5 月 31 日的考试


    \(\cal T_2\) 亿些整理

    Description

    给你一个有 \(2n\) 个点的二分图,两点之间有边或者没有边(没有边则输入 \(−1\)),询问是否存在一个权重为 \(k\) 的倍数的完美匹配。

    \(n,k\leq 100,-1\leq a_{i,j}<k\).

    Solution

    "完美匹配" 也就是选择一个排列 \(\{p\}\),使 \(i,p_i\) 可以匹配。这在提示我们行列式[1],一个朴素的想法是将矩阵的每个元素设为多项式

    \[M_{i,j} = \begin{cases} 0& f(i,j) = -1\\ x^{f(i,j)} & f(i,j) \not = -1 \end{cases} \]

    那么求行列式,注意要做关于 \(k\)循环卷积,最后检查第 \(0\) 项是否有值即可。但问题是求行列式的过程中有 \(-1\) 的系数,可能会抵消,所以需要再对每个元素赋一个随机权值。

    然而这是 \(\mathcal O(n^4\log n)\) 的,根本过不了,于是考虑优化。类似 \(\text{[CF917D] Stranger Trees}\) 一题,可以用单位根(匹配循环卷积)进行拉插,做到 \(\mathcal O(n^4)\),不过对于这种求 "\(k\) 倍数次项的系数和" 问题,可以考虑用单位根反演

    \[\begin{align}\text{Ans}&=\sum_{p}(-1)^{\sigma(p)}\cdot \left(\prod_{j=1}^n M_{j,p(j)}\right)\cdot [k\mid S]\\&=\sum_p(-1)^{\sigma(p)}\cdot \left(\prod_{j=1}^n M_{j,p(j)}\right)\cdot \left(\frac{1}{k}\cdot \sum_{j=0}^{k-1}\omega_{k}^{jS}\right)\\&=\frac{1}{k}\cdot \sum_{j=0}^{k-1}\sum_{p}(-1)^{\sigma(p)}\cdot \left(\prod_{q=1}^nM_{q,p(q)}\right)\cdot \omega_{k}^{jS}\\&=\frac{1}{k}\cdot \sum_{j=0}^{k-1}\sum_{p}(-1)^{\sigma(p)}\cdot \left(\prod_{q=1}^nM_{q,p(q)}\right)\cdot \left(\prod_{q=1}^n \omega_{k}^{j\cdot f(q,p(q))}\right)\\&=\frac{1}{k}\cdot \sum_{j=0}^{k-1}\sum_{p}(-1)^{\sigma(p)}\cdot \left(\prod_{q=1}^nM_{q,p(q)}\cdot \omega_{k}^{j\cdot f(q,p(q))\bmod k}\right)\end{align} \]

    复杂度仍然是 \(\mathcal O(n^4)\) 的,但是代码难度降低了很多(。

    关于单位根的求解,对每个 \(k\) 找到质数 \(p\) 满足 \(k\mid \varphi(p)\),再找到 \(g\)\(p\) 的原根即可。

    Code

    打原根表的时候日常忘记调用线性筛,交上去竟然还有 \(\text{70 pts}\),调了 \(114514\) 年。

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp]=x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <ctime>
    # include <random>
    # include <iostream>
    using namespace std;
    
    const int maxn = 105;
    const int M[] = {
        0, 998244853, 998244853, 998244853, 998244853, 998244911, 998244853, 998244899, 
        998244889, 998244991, 998244911, 998244853, 998244853, 998244911, 998244899, 998244991, 
        998245153, 998245169, 998244991, 998244991, 998245141, 998245207, 998244853, 998244943, 
        998244889, 998246101, 998244911, 998245189, 998245109, 998245483, 998244991, 998244889, 
        998245153, 998244853, 998245169, 998245571, 998245153, 998245349, 998244991, 998245483, 
        998245481, 998245697, 998245207, 998247323, 998244853, 998244991, 998244943, 998246537, 
        998245153, 998245543, 998246101, 998245543, 998245613, 998245037, 998245189, 998245711, 
        998245697, 998244991, 998245483, 998246371, 998245141, 998245481, 998244889, 998245207, 
        998245697, 998244911, 998244853, 998245463, 998245169, 998244943, 998245571, 998245091, 
        998245153, 998247553, 998245349, 998246101, 998246701, 998245403, 998245483, 998245739, 
        998246401, 998248213, 998245697, 998245483, 998245837, 998246461, 998247323, 998245483, 
        998245777, 998246251, 998244991, 998245613, 998245909, 998244889, 998246537, 998244991, 
        998245153, 998246401, 998245543, 998245711, 998246101
    };
    const int G[] = {
        0, 1, 998244852, 5074615, 329616108, 512917536, 5074616, 284946685, 
        778179908, 40688012, 688466749, 272550417, 201706028, 213728379, 217683281, 382768647, 
        131656641, 622090284, 84307224, 522511112, 984467961, 572345760, 469839584, 559749723, 
        960906257, 391474713, 120711819, 628911619, 41844055, 369886176, 583814719, 996639691, 
        489964959, 851930040, 774005272, 389987180, 438125926, 554869056, 722022053, 316886605, 
        188836165, 197218309, 85719001, 792442674, 163382638, 722023357, 147367773, 648068025, 
        952629146, 241980780, 421356778, 102132604, 479526832, 22553849, 937814972, 358653419, 
        440955809, 401158213, 623549242, 479991815, 660231564, 604930244, 358184273, 629450949, 
        354165161, 965774360, 398185037, 487018917, 321035413, 356274137, 409115192, 423197538, 
        563825846, 91584998, 181825537, 793662250, 494698896, 71618210, 527847884, 375777121, 
        919487911, 878988347, 868346131, 195667810, 143694432, 847292750, 974305740, 793990151, 
        115107639, 324111322, 625131606, 992985137, 499918120, 327632765, 186023662, 717664166, 
        213999155, 244067369, 365832161, 178332126, 487905538, 
    };
    
    int getFile() {
        freopen("sort.in","r",stdin);
        return true;
    }
    
    int File=getFile(), n=read(9), K=read(9);
    int a[maxn][maxn], _M[maxn][maxn], f[maxn][maxn];
    
    const int mod = M[K], g = G[K];
    
    int inv(int x,int y=mod-2,int r=1) {
        for(; y; y>>=1, x=1ll*x*x%mod)
            if(y&1) r=1ll*r*x%mod; return r;
    }
    int dec(int x,int y) { return x-y<0?x-y+mod:x-y; }
    int inc(int x,int y) { return x+y>=mod?x+y-mod:x+y; }
    
    int det() {
        int j, tmp, Inv, ret=1; bool f=0;
        for(int i=1;i<=n;++i) {
            for(j=i; !a[j][i] && j<=n; ++j);
            if(j>n) return 0;
            if(i^j) swap(a[i],a[j]), f^=1;
            Inv = inv(a[i][i]);
            ret = 1ll*ret*a[i][i]%mod;
            for(j=i+1;j<=n;++j) if(a[j][i]) {
                tmp = 1ll*Inv*a[j][i]%mod;
                for(int k=i;k<=n;++k)
                    a[j][k] = dec(a[j][k],1ll*tmp*a[i][k]%mod);
            }
        } return f? mod-ret: ret;
    }
    
    int main() {
        freopen("sort.out","w",stdout);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j) f[i][j]=read(9);
        mt19937 SEED(time(0)); int ans=0;
        uniform_int_distribution <int> gen(1,1e9);
        for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
            if(~f[i][j]) _M[i][j] = gen(SEED); 
        for(int k=0;k<K;++k) { 
            for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
                if(f[i][j]==-1) a[i][j]=0;
                else a[i][j] = 1ll*_M[i][j]*inv(g,1ll*k*f[i][j]%K)%mod;
            ans = inc(ans,det()); 
        } puts(ans? "Yes": "No");
        return 0;
    }
    

    \(\cal T_3\) 亿块田

    Description

    维护一个长度为 \(n\) 的序列 \(\{a\}\),初始值给定,有 \(m\) 次询问,需要支持三种操作:

    • 1 l r x,把 \(i\in[l,r]\)\(a_i\) 按位与上 \(x\)
    • 2 l r x,把 \(i\in[l,r]\)\(a_i\) 按位或上 \(x\)
    • 3 l r,询问 \(i\in[l,r]\)\(a_i\) 的最大值。

    \(n,m\leq 2\cdot 10^5,a_i,x<2^{20}\).

    Solution

    为啥永远都不会势能线段树啊喂

    先阐述做法,再分析复杂度。对线段树每个节点维护区间按位与、按位或、最大值和 \(\rm tag\)。以按位或修改 \((l,r,x)\) 为例,考虑二进制第 \(k\) 位,首先 \(x\) 的第 \(k\) 位为零是毫无效果的,若区间内所有数在这一位都是零且 \(x\) 的第 \(k\) 位为 \(1\),那么这个操作就变成了区间加,我们将区间加的值计入 \(\rm tag\) 里;反之,如果存在一个数在这一位为 \(1\),就不能直接区间加,先进行标记下传,再递归子区间。

    设势能函数 \(\Phi(o)=\sum_{i=0}^{\log V}f(o,i)\),其中 \(f(o,i)\) 表示节点 \(o\) 代表的区间所有数的第 \(i\) 位不全相等(也就是 $\rm bool $ 表达式),那么:

    • 对于每次修改,类似 \(\rm zkw\) 线段树的思想,从 \(l-1,r+1\) 代表的叶子节点往上走,当走到 \(p\) 满足两个儿子一个属于修改区间,一个不属于修改区间时,此次修改使 \(p\) 至多增加 \(\log V\) 的势能,由于 \(p\) 的级别为 \(\log n\) 个,所以单次修改 \(\Delta\Phi=\log n\log V\),所有修改增加 \(q\log n\log V\) 的势能;
    • 标记下传对势能没有影响;
    • 将修改区间在线段树上划分成 \(\log n\) 段,当一个节点往下递归时,它的势能至少减少 \(1\),至多减少 \(\log V\).

    区间初始势能为 \(n\log n\log V\),所以总复杂度为 \(\mathcal O((n+q)\cdot \log n\log V)\).

    Code

    Believe in yourself.
    

    1. 真的麻了,压根没想到行列式,我是 \(\rm joker\). ↩︎

  • 相关阅读:
    Caffe学习系列(9):solver优化方法
    Caffe学习系列(8):solver,train_val.prototxt,deploy.prototxt及其配置
    模式识别与图像处理笔试题
    图像增强与图像复原
    hough变换检测线和圆
    C++关键字:mutable(转)
    JS 时间格式化函数
    为当前的div 动态添加一个样式
    曾经感动过我们的文字 今天是否还有印象?——v1
    关于模态框 大小的设置 最大 中等 小小
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/16337050.html
Copyright © 2020-2023  润新知