集训队作业2018: 青春猪头少年不会梦到兔女郎学姐(多限制容斥)(生成函数)(组合数学)

本文主要是介绍集训队作业2018: 青春猪头少年不会梦到兔女郎学姐(多限制容斥)(生成函数)(组合数学),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题意

给定 n n n 种颜色的球,第 i i i 种颜色的球数量为 a i a_i ai 个,一种排列的贡献可以如下计算:先把这个序列首尾相连,然后把所有相邻且颜色相同的段拿出来,贡献为他们的长度之积,求所有排列的贡献和,原排列不同,首尾相连后相同的排列不算同一种。模 998244353 998244353 998244353


先考虑一个序列怎么做
我们对每一个 i i i 枚举将 a i a_i ai 分成 b i b_i bi 段,算出这种情况下的方案数已经每一种颜色的贡献
那么答案可以表示为(用 f ( a , b ) f(a,b) f(a,b) 表示把 a a a 分成 b b b 段的所有情况的系数之和)
∑ b w a y s ∗ ∏ f ( a i , b i ) \sum_{b}ways*\prod f(a_i,b_i) bwaysf(ai,bi)
发现 f f f 就是把 a a a 分成 b b b 块然后每一块选一个的方案数,巧妙地发现 f ( a , b ) = ( a + b − 1 b ∗ 2 − 1 ) = ( a + b − 1 a − b ) f(a,b)=\binom{a+b-1}{b*2-1}=\binom{a+b-1}{a-b} f(a,b)=(b21a+b1)=(aba+b1)
考虑求 w a y s ways ways,把所有颜色放在一起容斥,枚举强制合并的端点个数
w a y s = ∑ c ( ∏ ( b i − 1 c i ) ( − 1 ) c i ) ( ∑ b i − c i ) ! ∏ ( b i − c i ) ! ways=\sum_{c}(\prod \binom{b_i-1}{c_i}(-1)^{c_i})\frac{(\sum b_i-c_i)!}{\prod(b_i-c_i)!} ways=c((cibi1)(1)ci)(bici)!(bici)!
那么放到一起就是,换成枚举合并之后的段数
∑ b ∑ c ∏ f ( a i , b i ) ( b i − 1 c i − 1 ) ( − 1 ) b i − c i ( ∑ c i ) ! ∏ c i ! \sum_b\sum_c\prod f(a_i,b_i)\binom{b_i-1}{c_i-1}(-1)^{b_i-c_i}\frac{(\sum c_i)!}{\prod c_i!} bcf(ai,bi)(ci1bi1)(1)bicici!(ci)!
发现后面有一个 ∏ c i ! \prod c_i! ci!,考虑构造每一种颜色的 E G F EGF EGF
∑ c x c c ! ∑ b ≥ c f ( a i , b i ) ( b i − 1 c i − 1 ) ( − 1 ) b i − c i \sum_{c}\frac{x^c}{c!}\sum_{b\ge c}f(a_i,b_i)\binom{b_i-1}{c_i-1}(-1)^{b_i-c_i} cc!xcbcf(ai,bi)(ci1bi1)(1)bici
后面的一坨可以 N T T NTT NTT 求,然后分治 F F T FFT FFT 就可以全部乘起来

考虑环的情况怎么做
强制以 1 开头,那么 1 的生成函数的 x c x^c xc 会变成 x c − 1 x^{c-1} xc1
减去强制以 1 开头,以 1 结尾的个数,这个的 x c x^c xc 会变成 x c − 2 x^{c-2} xc2

但发现还是不对,如果环的循环是 T T T 的话,我们需要统计 T T T 遍,而实际上只统计了
b 1 ∑ a i T \frac{b_1}{\frac{\sum a_i}{T}} Taib1 遍,我们将第一个生成函数中含 b b b 的项除一个 b 1 b_1 b1,最后乘上 m m m 就可以了

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 2e5 + 5;
int read(){int cnt = 0, f = 1; char ch = 0;while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();return cnt * f;
}
#define poly vector<int>
#define pb push_back
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
void Add(int &a, int b){ a = add(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
int coe(int a){ return (a&1)?Mod-1:1; }
int ksm(int a, int b){ int ans=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; } 
int n, m, a[N];
int fac[N], ifac[N], inv[N];
poly f[N];
int C(int n, int m){ if(n<0||m<0||n<m) return 0; return mul(fac[n],mul(ifac[n-m],ifac[m])); }
void Fac_init(int n){fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;for(int i=2; i<=n; i++) inv[i]=mul(Mod-Mod/i,inv[Mod%i]);for(int i=2; i<=n; i++) fac[i]=mul(fac[i-1],i), ifac[i]=mul(ifac[i-1],inv[i]);
}
cs int K = 18;
poly w[K+1];
poly rev; int up, bit;
void init(int deg){up=1,bit=0; while(up<deg) up<<=1,++bit; rev.resize(up);for(int i=0; i<up; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void NTT_init(){for(int i=1; i<=K; i++) w[i].resize(1<<i-1);int wn=ksm(3,(Mod-1)/(1<<K)); w[K][0] = 1; for(int i = 1; i < (1<<K-1); i++) w[K][i]=mul(w[K][i-1], wn);for(int i = K-1; i; i--) for(int j=0; j<(1<<i-1); j++) w[i][j]=w[i+1][j<<1];
}
void NTT(poly &a, int typ){for(int i=0; i<up; i++) if(i<rev[i]) swap(a[i],a[rev[i]]);for(int i=1,l=1; i<up; i<<=1,++l)for(int j=0; j<up; j+=(i<<1))for(int k=0; k<i; k++){int x=a[k+j], y=mul(a[k+j+i],w[l][k]);a[k+j]=add(x,y); a[k+j+i]=dec(x,y);}if(typ==-1){reverse(a.begin()+1,a.end());for(int iv=ksm(up,Mod-2),i=0; i<up; i++) a[i]=mul(a[i],iv);}
}
poly operator * (poly a, poly b){int deg = a.size()+b.size()-1; init(deg); a.resize(up); b.resize(up); NTT(a,1); NTT(b,1);for(int i=0; i<up; i++) a[i]=mul(a[i],b[i]); NTT(a,-1); a.resize(deg); return a;
}
poly Solve(int l, int r){if(l==r){ for(int i=1; i<f[l].size(); i++) Mul(f[l][i], ifac[i]); return f[l]; } int mid=(l+r)>>1; return Solve(l,mid) * Solve(mid+1,r);
}
int main(){n = read();for(int i = 1; i <= n; i++) m+=a[i]=read();	Fac_init(m); NTT_init();for(int i = 1; i <= n; i++){poly g(a[i]+1, 0), h(a[i]+1, 0);for(int j = 1; j <= a[i]; j++) g[j]=mul(fac[j-1], C(a[i]+j-1,a[i]-j));if(i==1) for(int j=1; j<=a[i]; j++) Mul(g[j], inv[j]);for(int j=0; j<=a[i]; j++) h[a[i]-j] = mul(ifac[j], coe(j));g=g*h; f[i].resize(a[i]+1);for(int j=1; j<=a[i]; j++) f[i][j]=mul(g[a[i]+j], ifac[j-1]);}poly F=Solve(2,n);poly G(a[1],0);for(int i=0; i<G.size(); i++) G[i]=mul(f[1][i+1], ifac[i]);G=G*F;int ans=0;for(int i=1; i<G.size(); i++) Add(ans, mul(G[i],fac[i]));if(a[1]>1){G=poly(a[1]-1,0);for(int i=0; i<G.size(); i++) G[i]=mul(f[1][i+2], ifac[i]);G=G*F;for(int i=1; i<G.size(); i++) Dec(ans, mul(G[i],fac[i]));} cout << mul(ans, m); return 0;
}

这篇关于集训队作业2018: 青春猪头少年不会梦到兔女郎学姐(多限制容斥)(生成函数)(组合数学)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/658692

相关文章

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf