ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

8.11

2022-08-11 23:03:27  阅读:141  来源: 互联网

标签:int sum 卷积 bmatrix 8.11 eg define


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有