【CF715E】Complete the Permutations 第一类斯特林数

题目大意

  有两个排列 \(p,q\),其中有一些位置是空的。

  你要补全这两个排列。

  定义 \(s(p,q)\) 为 每次交换 \(p\) 中的两个数,让 \(p=q\) 的最小操作次数。

  求 \(s(p,q)=0,1,2,\ldots,n-1\) 的方案数。

  \(n\leq 300\)

题解

  考虑 \(s(p,q)\) 怎么求。

  对于每一个 \(i\),连一条有向边 \(p_i\to q_i\)。那么 \(s(p,q)\) 就是 \(n-\) 图中环的个数。

  先把 \(p,q\) 对应的图建出来,处理一下,算出 \(x\to ?,?\to x,x\to x,?\to ?\) 的边的个数,记为 \(s_1,s_2,s_3,s_4\)。

  有一个结论:如果一个环里面同时有前两种边,那么一定有第四种边。

  记 \(f_i\) 为用第一种边搞出了 \(i\) 个环的方案数

  枚举选了几条边,剩下的边就拿去和第四种边插在一起
\[
f_i=\sum_{j=i}^a\binom{a}{j}\begin{bmatrix}j\\i\end{bmatrix}\frac{(d+a-j-1)!}{(d-1)!}
\]
  \(g_i\) 为用第二种边搞出了 \(j\) 个环的方案数

  最后第四种边搞出 \(i\) 个环的方案数 是
\[
h_i=\begin{bmatrix}d\\i\end{bmatrix}
\]
  因为编号可以随便选,所以还要乘上 \(d!\)

  把这三个方案数卷在一起就好了。

  时间复杂度:\(O(n^2)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int N=310;
const ll p=998244353;
int fa[N],e1[N],e2[N];
int a[N],b[N];
int n;
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
    x=find(x);
    y=find(y);
    e2[x]++;
    if(x==y)
        return;
    e1[y]+=e1[x];
    e2[y]+=e2[x];
    fa[x]=y;
}
int s1,s2,s3,s4;
ll fac[N],ifac[N],inv[N];
ll f[N],g[N],h[N];
ll ans1[N],ans2[N];
ll s[N][N];
int deg[N];
int c[N];
int c1[N],c2[N],d1[N],d2[N];
ll binom(int x,int y)
{
    return fac[x]*ifac[y]%p*ifac[x-y]%p;
}
int main()
{
    open("cf715e");
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        e1[i]=1;
    }
    for(int i=1;i<=n;i++)
        if(a[i]&&b[i])
            merge(a[i],b[i]);
    for(int i=1;i<=n;i++)
    {
        if(a[i]&&find(a[i])==find(b[i])&&e1[a[i]]==e2[a[i]])
        {
            if(find(a[i])==a[i])
                s3++;
        }
        if(a[i])
            a[i]=find(a[i]);
        if(b[i])
            b[i]=find(b[i]);
        if(a[i]&&b[i]!=a[i])
        {
            c1[a[i]]=1;
            d1[a[i]]=bool(b[i]);
        }
        if(b[i]&&a[i]!=b[i])
        {
            c2[b[i]]=1;
            d2[b[i]]=bool(a[i]);
        }
    }
    for(int i=1;i<=n;i++)
        if(!a[i]&&!b[i])
            s4++;
    for(int i=1;i<=n;i++)
    {
        if(c1[i]&&c2[i]&&!d1[i]&&!d2[i])
            s4++;
        else
        {
            if(c1[i]&&!d1[i])
                s1++;
            if(c2[i]&&!d2[i])
                s2++;
        }
    }
    fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1;
    for(int i=2;i<=n;i++)
    {
        inv[i]=(-p/i*inv[p%i]%p+p)%p;
        fac[i]=fac[i-1]*i%p;
        ifac[i]=ifac[i-1]*inv[i]%p;
    }
    s[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            s[i][j]=(s[i-1][j-1]+s[i-1][j]*(i-1))%p;
    if(s4)
    {
        for(int i=0;i<=s1;i++)
            for(int j=i;j<=s1;j++)
                f[i]=(f[i]+binom(s1,j)*s[j][i]%p*fac[s4+s1-j-1]%p*ifac[s4-1])%p;
        for(int i=0;i<=s2;i++)
            for(int j=i;j<=s2;j++)
                g[i]=(g[i]+binom(s2,j)*s[j][i]%p*fac[s4+s2-j-1]%p*ifac[s4-1])%p;
        for(int i=0;i<=s4;i++)
            h[i]=s[s4][i]*fac[s4]%p;
    }
    else
    {
        for(int i=0;i<=s1;i++)
            f[i]=s[s1][i];
        for(int i=0;i<=s2;i++)
            g[i]=s[s2][i];
        h[0]=1;
    }
    for(int i=0;i<=s1;i++)
        for(int j=0;j<=s2;j++)
            ans1[i+j]=(ans1[i+j]+f[i]*g[j])%p;
    for(int i=0;i<=s1+s2;i++)
        for(int j=0;j<=s4;j++)
            ans2[i+j]=(ans2[i+j]+ans1[i]*h[j])%p;
    for(int i=n;i>=1;i--)
        printf("%lld ",i-s3>=0?ans2[i-s3]:0);
    return 0;
}

原文地址:https://www.cnblogs.com/ywwyww/p/9892125.html

时间: 2024-11-13 01:56:28

【CF715E】Complete the Permutations 第一类斯特林数的相关文章

HDU3625(SummerTrainingDay05-N 第一类斯特林数)

Examining the Rooms Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1661    Accepted Submission(s): 1015 Problem Description A murder happened in the hotel. As the best detective in the town, yo

hdu 3625 第一类斯特林数

题目链接:click here Examining the Rooms Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1146    Accepted Submission(s): 689 Problem Description A murder happened in the hotel. As the best detective

HDU 3625 Examining the Rooms(第一类斯特林数)

题意:给你n扇门,然后可以开k次门,每次们后都有一把钥匙,不能开1号门,问最后打开一号门的概率是多少 思路:看大家说是裸的第一类斯特林数,我觉得有篇博客写的很好(传送门),这里采取的是博客里的第二种思路,感觉这种如果想到的话更容易理解,但是并没有遇到博客里说的g++会wa的情况 代码: #include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=55; LL s1[maxn][maxn];

hdu 3625 Examining the Rooms —— 第一类斯特林数

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3625 学习斯特林数:https://blog.csdn.net/qq_33229466/article/details/75042895 https://www.cnblogs.com/gzy-cjoier/p/8426987.html http://www.cnblogs.com/zhouzhendong/p/Stirling-Number.html 关于这道题: 得到一把钥匙后,可以连续开的门与钥匙

【2019雅礼集训】【第一类斯特林数】【NTT&amp;多项式】permutation

目录 题意 输入格式 输出格式 思路: 代码 题意 找有多少个长度为n的排列,使得从左往右数,有a个元素比之前的所有数字都大,从右往左数,有b个元素比之后的所有数字都大. n<=2*10^5,a,b<=n 输入格式 输入三个整数n,a,b. 输出格式 输出一个整数,表示答案. 思路: 这道题是真的神啊... 首先,根据官方题解的思路,首先有一个n^2的DP: 定义dp[i][j]表示一个长度为i的排列,从前往后数一共有j个数字大于所有排在它前面的数字. 首先有转移式: \[dp[i][j]=d

第一类斯特林数

第一类斯特林数大概是这样一个意思:首先从n排列中选出一个m圆排列,这个圆排列也可以理解为有一个位置元素固定的排列,其方案数显然为(m-1)!. 可以用来解决一下有特殊位置的计数问题. 例题 [FJOI2016]建筑师 首先找到最高的建筑作为分水岭后,剩下的位置可以如下划分成a-1+b-1个区间. 发现这a-1+b-1个区间第每一个都必须满足区间最大值放在最前面. 这个东西就可以用第一类斯特林数解决. https://www.cnblogs.com/zhouzhendong/p/Stirling-

CF960G(第一类斯特林数)

设\(f(i,j)\)为\(i\)个数的序列,有\(j\)个前缀最大值的方案数 我们考虑每次添一个最小数,则有:\(f(i,j)=f(i-1,j)+(i-1)*f(i-1,j-1)\),显然这是第一类斯特林数 从而我们得到一个朴素的答案:\[Ans=\sum\limits_{i=1}^{n}f_{i,a-1}×f_{n-1-i,b-1}×C_{n-1}^i\] 理解:枚举\(i+1\)为最大值添的位置,则已经限制了前缀最大值个数及后缀最大值个数,然后再乘上前半部分所填的数 观察\(f_{i,a-

[CF715E] Complete the Permutations(dp+组合计数)

Problem 给定两个长度为 \(n\) 的排列 \(a,b\),但是其中有些位置未知,用 \(0\) 表示. 定义两个排列的距离为:每次选择 \(a\) 中的两个元素交换,使其变为 \(a\) 的最小次数. 要求补全两个排列,求补全之后 \(a,b\) 距离为 \(i\) \((i∈[0,n-1])\) 的方案数. \(n ≤ 250\),答案对 \(998244353\) 取模. Solution 先考虑怎么算补全之后 \(a,b\) 的距离: 对于每个 \(i\) \((i∈[1,n])

CF717A Festival Organization(第一类斯特林数,斐波那契数列)

题目大意:求 $\sum\limits_{n=l}^{r}\dbinom{f_n}{k}\bmod 10^9+7$.其中 $f_n$ 是长度为 $n$ 的 $01$ 序列中,没有连续两个或超过两个 $0$ 的个数. $1\le k\le 200,1\le l\le r\le 10^{18}$. 先考虑如何求 $f_n$. 令 $g[i][j]$ 表示长度为 $i$,结尾是 $j$ 的序列个数. $$g[i][0]=g[i-1][1]$$ $$g[i][1]=g[i-1][0]+g[i-1][1]