其他分享
首页 > 其他分享> > 8.11

8.11

作者:互联网

k进制FWT

定义\(k\)维或、与、异或运算

\[x\ or\ y=\sum_i max(x_i,y_i)k^i\\ x\ and\ y=\sum_i min(x_i,y_i)k^i\\ x\ xor\ y=\sum_i ((x_i+y_i)\%k)\ k^i \]

什么叫不进位加法啊.jpg

\(FWT\)本质是要把当前是幂集合当成一个向量,系数变换当作一个矩阵,那么要满足

\[\vec{F}A·\vec{G}A=(\vec{F*G})A \]

左边点乘表示逐位相乘,右边表示卷积。

那么构造对应的变换时,原本的或卷积是子集和,现在的\(k\)进制或卷积就是高维前缀和。

那么或卷积的矩阵就类似于

\[\begin{bmatrix} a_0&a_1 & \cdots & a_{k-1} \\ \end{bmatrix} \begin{bmatrix} 1 &1&1 & \cdots & 1 \\ 0& 1&1 &\cdots & 1 \\ 0&0&1&\cdots & 1\\ \vdots & \vdots & \vdots & \vdots& \vdots\\ 0 &0&0 & \cdots & 1 \end{bmatrix} = \begin{bmatrix} a_0&a_0+a_1 & \cdots & \sum_{i=0}^{k-1}a_{i} \\ \end{bmatrix} \]

并卷积就是后缀和。

异或卷积就比较麻烦。

这里鸣谢万弘的讲解。

对于二维的异或卷积,我们之前用的矩阵是这样的

\[\begin{bmatrix} a_0&a_1\\ \end{bmatrix} \begin{bmatrix} 1&1\\ 1&-1 \end{bmatrix} = \begin{bmatrix} a_0+a_1,a_0-a_1 \end{bmatrix} \]

这个矩阵满足什么性质?

其实这个过程就是\(FFT/NTT\)的过程,其一单位根/原根具有循环意义

\(x\ xor\ y=z\Leftrightarrow \omega_k^x*\omega_k^y=\omega_k^z\)

我们\(FFT\)做的本来就是循环卷积。每次卷积时把长度延长一倍正是为了不让多出来的部分卷回去,而异或卷积恰好是要让溢出的部分卷回前面。

所以在异或卷积里,\(A\)矩阵就是范德蒙德矩阵。它的逆矩阵我们也见过,类似\(FFT\)一样把原矩阵代回去再除以二就行。

值得一提的是\(k=2\)时,范德蒙德矩阵恰好是

\[\begin{bmatrix} 1&1\\ 1&-1 \end{bmatrix} \]

所以二维(多维)异或\(FWT\)还有一种形式,就是变换和逆变换一样做,逆变换结束之后再除以序列长度。

CF453D

题意:

\(e_i[u]=\sum_ve_{i-1}[v]\cdot b[f(u,v)]\) 。这里 \(b[]\) 称作变换系数——一个有 \(m+1\) 个元素的数组。而 \(f(u,v)\) 为二进制数 \((u\;xor\;v)\) 中 \(1\) 的个数。

给定变换系数 \(b[]\) 和在时间 \(0\) 时的初始能量分布 \(e_0[]\) 。帮助暮光闪闪预测在时刻 \(t\) 时的能量分布。答案可能非常大,你只要输出答案除以 \(p\) 的余数即可。

题解:

设\(c[x]=b[popcount(x)]\)

\[e_i[u]=\sum_ve_{i-1}[v]*c[x]*[u\oplus v==x]\\ e_i[u]=\sum_ve_{i-1}[v]*c[x]*[x\oplus v==u]\\ e_t=e_0*c^t \]

其中\(*\)是异或卷积。

考虑用快速幂求\(c^t\),\(fwt\)不用每次都做,只用先做正变换,然后把点值求\(t\)次幂,再逆变换回来。

但是如果\(p\)不是奇数,将没有\(2\)的逆元。

参考上面的\(FWT\)上面可以不用求\(2\)的逆元,但是可能也没有长度\(n\)的逆元。

但是这里\(2\)的逆元和\(n\)的逆元的用途是不同的:

\(2\)的逆元要在运算中参与取模,而\(n\)的逆元只在\(fwt\)的最后来做,所以可以利用这个定理

\[a\equiv b\ (mop\ p)\Leftrightarrow a*n\equiv b*n\ (mod\ p*n) \]

提前把模数乘以\(n\),然后就可以在最后直接除以\(n\)得到答案了。

\#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=998244353,inv2=499122177,inf=2e18;
    void __init(int n=2000) {}
    
    inline void main()
    {
        int n,m,t,mod;
        cin>>n>>t>>mod;
        m=(1<<n);
        mod*=m;
        vector<int> a(m),b(n+1),c(m);
        for(int i=0;i<m;++i) cin>>a[i];
        for(int i=0;i<=n;++i) cin>>b[i];
        for(int i=0;i<m;++i)
        {
            c[i]=b[__builtin_popcount(i)];
        }
        auto fast=[&](int x,int k) -> int
        {
            int ret=1;
            while(k)
            {
                if(k&1) ret=(__int128)ret*x%mod;
                x=(__int128)x*x%mod;
                k>>=1;
            }
            return ret;
        };
        auto fwt=[&](vector<int> &a,int inv) -> void
        {
            for(int k=1;2*k<=m;k<<=1)
            {
                for(int i=0;i<m;i+=2*k)
                {
                    for(int j=0;j<k;++j)
                    {
                        int x=a[i+j],y=a[i+j+k];
                        a[i+j]=(x+y)%mod;
                        a[i+j+k]=(x-y+mod)%mod;
                    }
                }
            }
            if(inv)
            {
                for(int i=0;i<m;++i) a[i]/=m;
            }
        };
        fwt(c,0);fwt(a,0);
        for(int i=0;i<m;++i)
        {
            a[i]=(__int128)a[i]*fast(c[i],t)%mod;
        }
        fwt(a,1);
        for(int i=0;i<m;++i) cout<<a[i]<<'\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    red::__init();
    int qwq=1; //cin>>qwq;
    while(qwq--) red::main();
    return 0;
}
/*

*/

CF1494F

题意:

给定一个 \(n\) 个顶点和 \(m\) 条边组成的无向连通图。你的目标是破坏给定图形的所有边。

可以选择任何顶点作为起始顶点,开始沿边行走。当你走过一条边时,这条边会被破坏,不能走被破坏的边。

最多可以在某顶点执行进行一次模式切换,模式转换后,按以下方式删除经过的边:模式转换后第一条边不被破坏,第二条边被破坏,第三条边不被破坏,第四条边被破坏,依此类推。不能切换回原始模式,可以不执行此操作。

\(n,m \le 3000, m\le \dfrac{n(n-1)}{2}\)。

题解:

第一个条件是说走过的路不能回头,那么第一阶段走的路径一定是欧拉路径。

第二阶段隔一次破坏一次。而且最后一条边一定被删除。

那么最后一条边就是在反复走。由此倒推得出在第二阶段,所有边都是反复走来销毁的,也就是说销毁的是一张菊花图。

所以总的路径是由一条欧拉路径加一个菊花图构成的。

如果原图就是一个欧拉路径就直接走。

如果不是,考虑枚举菊花图的中心在哪里,那么剩下如果满足要求,只有两种可能性:

除了菊花中心和邻点外,还有一个奇数度的点:那么以这个点和菊花中心为欧拉路径端点遍历,然后再在菊花图上走。

这里有一点需要判断的:

像这种情况,即使去掉\(3\)的邻点和\(3\)本身之后只有一个点是奇数度,也不能遍历,所以在遍历欧拉路径后,要判断欧拉路径的长度是否等于中心和邻点之外的边的总长度。

第二种情况:

除了菊花中心和邻点外,没有奇数度的点:

选一个奇数度的点作为起点,中心作为终点。

但还是有这种情况:比如上图去掉\(1,2\),那么你必须从\(4\)号点出发才能获得正确的答案。

讨论办法是枚举起点,然后仍然判断欧拉路径长度。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=998244353,inv2=499122177,inf=2e18;
    void __init(int n=2000) {}
    
    inline void main()
    {
        int n,m;
        cin>>n>>m;
        vector<int> rd(n+1);
        vector<set<int> > eg(n+1);
        vector<set<int> > eg2(n+1);
        for(int i=1;i<=m;++i)
        {
            int x,y;cin>>x>>y;
            ++rd[x],++rd[y];
            eg[x].insert(y);
            eg[y].insert(x);
        }
        int sum=0,st=0,ed=0;
        for(int i=1;i<=n;++i)
        {
            eg2[i]=eg[i];
            if(rd[i]&1)
            {
                if(!st) st=i;
                else ed=i;
                ++sum;
            }
        }
        if(!sum) st=1;
        int rt;
        vector<int> sta;
        vector<bool> vis(n+1);
        function<void(int)> dfs=[&](int now) -> void
        {
            while(!eg[now].empty())
            {
                int t=*eg[now].begin();
                eg[now].erase(t);
                eg[t].erase(now);
                dfs(t);
            }
            sta.emplace_back(now);
        };
        if(sum<=2)
        {
            dfs(st);
            reverse(sta.begin(),sta.end());
            cout<<sta.size()<<'\n';
            for(int t:sta) cout<<t<<' ';
            return;
        }
        for(int x=1;x<=n;++x)
        {
            rt=x;
            int ss=sum,tot=0;
            for(int i=1;i<=n;++i)
            {
                vis[i]=0;
            }
            for(int t:eg[x])
            {
                if(rd[t]&1)
                {
                    --ss;
                    vis[t]=1;
                    ++tot;
                }
            }
            if(rd[x]&1) --ss;
            if(ss>=2) continue;
            set<int> q;
            q=eg[x];
            if(ss==1)
            {
                for(int i=1;i<=n;++i)
                {
                    if(vis[i])
                    {
                        eg[x].erase(i);
                        eg[i].erase(x);
                        continue;
                    }
                    if((rd[i]&1)&&i!=x) st=i;
                }
                for(int j=1;j<=n;++j) eg2[j]=eg[j];
                sta.clear();
                dfs(st);
                reverse(sta.begin(),sta.end());
                int tmp=sta.size();
                //cout<<tmp<<"!!"<<endl;
                if(tmp!=m-tot+1)
                {
                    for(int j=1;j<=n;++j) eg[j]=eg2[j];
                    continue;
                }
                cout<<sta.size()+1+2*tot<<'\n';
                for(int t:sta) cout<<t<<' ';
                cout<<"-1 ";
                for(int t:q)
                {
                    if(vis[t])
                    {
                        cout<<t<<' '<<x<<' ';
                    }
                }
                return;
            }
            --tot;
            vector<int> qaq;
            for(int i=1;i<=n;++i)
            {
                if(vis[i]&&i!=x)
                {
                    qaq.emplace_back(i);
                    eg[x].erase(i);
                    eg[i].erase(x);
                    
                }
            }
            for(int i:qaq)
            {
                if(!vis[i]||i==x) continue;
                int st=i,ed=x;
                //cout<<x<<' '<<i<<"!!"<<endl;
                vis[i]=0;
                for(int j=1;j<=n;++j) eg2[j]=eg[j];
                eg[ed].insert(st);
                eg[st].insert(ed);
                sta.clear();
                dfs(st);
                reverse(sta.begin(),sta.end());
                int tmp=sta.size();
                //cout<<x<<' '<<i<<' '<<tmp<<"!!"<<endl;
                if(tmp!=m-tot+1)
                {
                    for(int j=1;j<=n;++j) eg[j]=eg2[j];
                        vis[i]=1;
                    continue;
                }
                cout<<sta.size()+1+2*tot<<'\n';
                for(int t:sta) cout<<t<<' ';
                cout<<"-1 ";
                for(int t:q)
                {
                    if(vis[t])
                    {
                        cout<<t<<' '<<x<<' ';
                    }
                }
                return;
            }
            
        }
        cout<<0<<'\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    red::__init();
    int qwq=1; //cin>>qwq;
    while(qwq--) red::main();
    return 0;
}
/*

*/

标签:int,sum,卷积,bmatrix,8.11,eg,define
来源: https://www.cnblogs.com/knife-rose/p/16578197.html