当前位置: 首页 > news >正文

21年icpc上海区域赛B题Strange Permutations (容斥+生成函数)

题意: 求给定n的排列P,求满足对于任意i∈[1,n-1]满足 Q i + 1 ≠ P Q i Q_{i+1}\neq P_{Q_{i}} Qi+1=PQi的Q排列方案数。(n<=1e5)
Solution: 排列的图意义

\quad 直接看这个式子有点晕,看到排列可以从图意义入手。
我们设P,Q的图意义为: P Q i − > Q i + 1 P_{Q_{i}}->Q_{i+1} PQi>Qi+1,当然反过来也一样的(这个意义想了几天,被 i − > P i + 1 i->P_{i+1} i>Pi+1的固有套路给框柱了,意义是可以自定义的)。如果考虑i从1到n的话,连出来肯定是若干环,这个和 i − > P i i->P_{i} i>Pi的建图意义是一样的。
\quad 乱证一下:因为 P Q i P_{Q_i} PQi是个排列, Q i + 1 Q_{i+1} Qi+1也是个排列(先假设Q{n+1}有意义),把两个排列按照 Q i + 1 Q_{i+1} Qi+1大小排序,就转化成了 i − > P i i->P_i i>Pi
\quad 然后因为i只从1到n-1,那图是个哈密顿图(哈密顿路径)。因为只有n-1个点有出边,n-1个点有入边,且出入度至多为1,画画图得连出来是个哈密顿图。或者从上面连出来的环意义考虑,首先环是不合法的,因为有环就意味这每个点入出度都为1,显然不对,那还要联通,只能是哈密顿图了。
\quad 说了这么多就想证,图意义是哈密顿图。那问题转化成图上有n条禁边,求合法的哈密顿路径数量。禁边的可以设成(i,Pi)。
\quad 为什么这样设是对的呢,上面证了i->Pi的建图意义和 P Q i − > Q i + 1 ( 假设 Q n + 1 的意义是 Q 1 且 Q 1 不存在 ) P_{Q_{i}}->Q_{i+1}(假设Q_{n+1}的意义是Q_1且Q_1不存在) PQi>Qi+1(假设Qn+1的意义是Q1Q1不存在)等价,那反过来也是等价的。
直观点,我们列出 P Q i P_{Q_i} PQi Q i + 1 Q{i+1} Qi+1
P:3 4 1 2
Q:1 4 3 2
P Q i P_{Q_i} PQi:3 2 1 4
Q i + 1 Q_{i+1} Qi+1: 4 3 2 ?
根据上面的建图意义,这是合法的。
来组不合法的。
P:3 4 1 2
Q:1 3 4 2
P Q i P_{Q_i} PQi:3 2 1 4
Q i + 1 Q_{i+1} Qi+1:3 4 2 ?
3指向3不合法了,发现用 i − > P i i->P_i i>Pi与这个建图意义的禁边意义是相同的!于是就可以转成n条禁边(i,Pi)。而n条禁边组成了若干环,我们要选的是哈密顿回路,所以不能有环。
\quad 考虑容斥, f ( i ) f(i) f(i)表示钦定选i条禁边的哈密顿路径数,因为不能有环,所以对于禁边组成的若干环,每个环上的边都不能全取,则 a n s = ∑ i = 0 n − 1 ( − 1 ) i ∗ f ( i ) ans=\sum_{i=0}^{n-1}(-1)^i*f(i) ans=i=0n1(1)if(i)(这是二项式反演,但不是没有组合数的容斥系数!容斥系数在后面的多项式计算里体现出来了!所以最外层不用带组合数!!!)。
设每个环多项式为 g ( x ) = ∑ i = 0 s i z − 1 C ( s i z , i ) x i g(x) = \sum_{i=0}^{siz-1}C(siz,i)x^i g(x)=i=0siz1C(siz,i)xi,其中siz为环大小。
设 F = ∏ g j , f ( i ) = [ x i ] F 设F =\prod g_j, f(i)=[x^i]F F=gj,f(i)=[xi]F,即第i项的系数,F的计算用启发式合并算一下就可以了。
时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
这题难点还是在求解问题的转化上。搞清楚后,很容易。
(因为这题结合别的大佬们的题解理解了好久,把一些自己想错的问题都写了,所以比较长,还有一些证明和理解也是自己瞎想的,如有不对的话,还请读者留言区指正,感谢)

#defien int long long
const int N=1e5+5;
const int mod=998244353;
const double eps=1e-8;
const int MAXL = 60;
int n,k;
int a[N];
std::vector<int> G[N];
const int maxn = 6e6+10;
const int p = 998244353;
int Pow(int x,int d){
    int tans = 1;
    if(d == 0)return 1%p;
    int a = Pow(x,d/2);
    tans = 1ll*a*a%p;
    if(d%2)tans = 1ll*tans*x%p;
    return tans%p;
}
typedef vector<int> Poly;//多项式定义
int F1[maxn],F2[maxn];
int rev[maxn];
void NTT(int * A,int lim,int opt) {
    int i, j, k, m, gn, g, tmp;
    for(int i = 0; i < lim; ++i)rev[i] = (i & 1)*(lim >> 1) + (rev[i >> 1] >> 1);
    for(i = 0; i < lim; ++i)if (rev[i] < i) swap(A[i], A[rev[i]]);
    for(m = 2; m <= lim; m <<= 1) {
        k = m >> 1;
        gn = Pow(3,(p - 1) / m);
        for(i = 0; i < lim; i += m) {
            g = 1;
            for (j = 0; j < k; ++j, g = 1ll * g * gn % p) {
                tmp = 1ll * A[i + j + k] * g % p;
                A[i + j + k] = (A[i + j] - tmp + p) % p;
                A[i + j] = (A[i + j] + tmp) % p;
            }
        }
    }
    if(opt == -1){
        reverse(A+1,A+lim);
        int inv = Pow(lim,p-2);
        for(i = 0; i < lim; ++i) A[i] = 1ll * A[i] * inv % p;
    }
}
Poly operator + ( const Poly &A,const Poly &B){
    int n = A.size() , m = B.size();
    int siz = max(n,m);
    Poly C(siz,0);
    for(int i=0 ;i<siz ;i++){
        if( i <n && i < m) C[i] = (A[i] + B[i])%p;
        else if( i < n ) C[i] = A[i];
        else C[i] = B[i];
    }
    return C;
}
Poly mul(const Poly & A,const Poly & B){
    int n = A.size(),m = B.size();
    int siz = n + m - 1;
    Poly C(siz);
    if(siz < 64){//小于等于64项直接暴力算
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++)C[i+j] = (C[i+j] + 1LL*A[i]*B[j]%p)%p;
        }
        return C;
    }
    int fsiz = 1;
    while(fsiz <= siz)fsiz <<=1;
    for(int i = 0;i < fsiz;i++)F1[i] = F2[i] = 0;
    for(int i = 0;i < n;i++)F1[i] = A[i];
    for(int i = 0;i < m;i++)F2[i] = B[i];
    NTT(F1,fsiz,1);
    NTT(F2,fsiz,1);
    for(int i = 0;i < fsiz;i++)F1[i] = 1ll*F1[i]*F2[i]%p;
    NTT(F1,fsiz,-1);
    for(int i = 0;i < siz;i++){
        C[i] = F1[i];
    }
    return C;
}
int fac[N],ni_f[N];
ll qsm(int a,int b){
    ll ans=1,tmp=a;
    while( b ) {
        if( b&1 ) ans = ans * tmp%mod;
        tmp = tmp * tmp%mod;
        b>>=1;
    }
    return ans;
}
void ini(){
    int maxn = 1e5;
    fac[0]=1;
    _for(i,1,maxn) fac[i] =  (fac[i-1] * i)%mod;
    ni_f[maxn] = qsm(fac[maxn],mod-2);
    _rep(i,maxn-1,0) ni_f[i] = ni_f[i+1] * (i+1)%mod;
}
ll C(int n,int m){
    if( m==n || m==0 ) return 1;
    if( n < m ) return 0;
    return fac[n] * ni_f[n-m]%mod * ni_f[m]%mod;
}
int vis[N];
int dfs(int x ,int fa){
    vis[x]=1;
    int siz = 1;
    for(int y:G[x]){
        if( y==fa || vis[y]) continue;
        siz += dfs(y,x);
    }
    return siz;
}
vector<int> V;
Poly cal(int l,int r){
    if( l==r ){
        Poly ans(V[l]+1,0);
        _for(i,0,V[l]-1){
            ans[i] = C(V[l],i);
        }
        return ans;
    }
    int mid = (l+r)>>1;
    return mul(cal(l,mid) , cal(mid+1,r));
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    IOS;
    ini();
    cin>>n;
    _for(i,1,n){
        cin>>a[i];
        G[i].push_back(a[i]);
        G[a[i]].push_back(i);
    }
    _for(i,1,n){
        if( !vis[i] ){
            int siz = dfs(i,0);
            V.push_back(siz);
        }
    }
    Poly F = cal(0,V.size()-1);
    int ans = 0;
    for(int i=0 ;i<F.size() ;i++){
        int flag = i&1?-1:1;
        ans = (ans + mod + flag * F[i] * fac[n-i]%mod)%mod;
    }
    cout<<ans<<endl;
    AC; 
}

相关文章:

  • 【CSS】QQ邮箱布局,词典四列布局,行内布局
  • 滑动窗口问题
  • Java的输入 Scanner in=new Scanner(System.in);
  • 接口请求返回状态码
  • Cocos Creator游戏引擎
  • [静态时序分析简明教程(一)] 绪论
  • ADU87、DCB20X-E等设备以RTSP方式添加第三方相机或录像机教程
  • 计算机网络常见面试题汇总(建议收藏)
  • 【从零带你玩转Linux】Linux环境搭建
  • AMBA总线相关知识记录
  • vscode快速安装教程
  • 34、Java 中有了基本数据类型,为什么还需要有包装类型?包装类型是啥?
  • Spring In Action 5 学习笔记 chapter8 RabbitMQ(AMQP)要点
  • K8s有状态应用(StatefulSet)之Mysql集群
  • 原生table动态数据,通过jQuery实现相同数据时候跨行合并
  • $translatePartialLoader加载失败及解决方式
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Angular 2 DI - IoC DI - 1
  • IDEA常用插件整理
  • iOS | NSProxy
  • Javascript编码规范
  • Joomla 2.x, 3.x useful code cheatsheet
  • Python_OOP
  • ReactNative开发常用的三方模块
  • Swoft 源码剖析 - 代码自动更新机制
  • uva 10370 Above Average
  • 阿里云购买磁盘后挂载
  • 闭包--闭包之tab栏切换(四)
  • 分布式熔断降级平台aegis
  • 力扣(LeetCode)357
  • 微信小程序填坑清单
  • 项目实战-Api的解决方案
  • 新版博客前端前瞻
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • 整理一些计算机基础知识!
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (附源码)计算机毕业设计ssm电影分享网站
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转载)PyTorch代码规范最佳实践和样式指南
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .Net IE10 _doPostBack 未定义
  • .NET 中各种混淆(Obfuscation)的含义、原理、实际效果和不同级别的差异(使用 SmartAssembly)
  • .Net的C#语言取月份数值对应的MonthName值
  • /etc/sudoer文件配置简析
  • @Valid和@NotNull字段校验使用
  • [ 数据结构 - C++] AVL树原理及实现