正题

题目链接:https://loj.ac/p/6503


题目大意

\(n\)张卡\(m\)种,第\(i\)种卡有\(a_i\)张,求所有排列中有\(k\)对相邻且相同的卡牌。

\(1\leq n\leq 10^5,0\leq k\leq 10^5,1\leq m\leq 20000,\sum_{i=1}^ma_i=n\)


解题思路

\(k\)对相邻的相同,就是可以分成有\(n-k\)组相同的。

考虑这个问题,把每组牌分成若干组插到不同位置,先不考虑这样可能插到相邻位置的情况我们后面可以再用容斥消掉。

那么对于一个\(a\),分成\(i\)组的方案就是\(\binom{a-1}{i-1}\),因为排列,列出生成函数\(\sum_{i=1}^a\binom{a-1}{i-1}\frac{x^i}{i!}\)。

然后用分治\(NTT\)乘起来,最后第\(i\)项乘上一个\(i!\)就是方案了。

然后考虑容斥,枚举一个\(i\leq n-k\)然后相当于至少有\(k\)对相邻,现在要减去更多的,所以容斥系数就是\((-1)^{n-k-i}\binom{n-i}{k}\)

时间复杂度\(O(n\log n\log m)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e5+10,T=19,P=998244353;
struct Poly{
ll a[N<<2],n;
}F[T+1];bool v[T+1];
ll m,n,k,w[N],inv[N],fac[N],r[N],x[N],y[N];
ll power(ll x,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*x%P;
x=x*x%P;b>>=1;
}
return ans;
}
void NTT(ll *f,ll n,ll op){
for(ll i=0;i<n;i++)
if(i<r[i])swap(f[i],f[r[i]]);
for(ll p=2;p<=n;p<<=1){
ll len=p>>1,tmp=power(3,(P-1)/p);
if(op==-1)tmp=power(tmp,P-2);
for(ll k=0;k<n;k+=p){
ll buf=1;
for(ll i=k;i<k+len;i++){
ll tt=buf*f[i+len]%P;
f[i+len]=(f[i]-tt+P)%P;
f[i]=(f[i]+tt)%P;
buf=buf*tmp%P;
}
}
}
if(op==-1){
ll invn=power(n,P-2);
for(ll i=0;i<n;i++)
f[i]=f[i]*invn%P;
}
return;
}
void Mul(Poly &F,Poly &G){
ll n=1;
while(n<F.n+G.n-1)n<<=1;
for(ll i=0;i<F.n;i++)x[i]=F.a[i];
for(ll i=0;i<G.n;i++)y[i]=G.a[i];
for(ll i=F.n;i<n;i++)x[i]=0;
for(ll i=G.n;i<n;i++)y[i]=0;
for(ll i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)?(n>>1):0);
NTT(x,n,1);NTT(y,n,1);
for(ll i=0;i<n;i++)x[i]=x[i]*y[i]%P;
NTT(x,n,-1);
for(ll i=0;i<n;i++)F.a[i]=x[i];
F.n=F.n+G.n-1;return;
}
ll FindA(){
for(ll i=0;i<T;i++)
if(!v[i]){v[i]=1;return i;}
}
ll C(ll n,ll m)
{return fac[n]*inv[m]%P*inv[n-m]%P;}
ll NTT(ll l,ll r){
if(l==r){
ll x=FindA();
for(ll i=1;i<=w[l];i++)
F[x].a[i]=inv[i]*C(w[l]-1,i-1)%P;
F[x].n=w[l]+1;return x;
}
ll mid=(l+r)>>1;
ll ls=NTT(l,mid),rs=NTT(mid+1,r);
Mul(F[ls],F[rs]);v[rs]=0;return ls;
}
signed main()
{
scanf("%lld%lld%lld",&m,&n,&k);inv[1]=1;
for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P;
fac[0]=inv[0]=1;
for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P;
for(ll i=1;i<=m;i++)scanf("%lld",&w[i]);
ll p=NTT(1,m),ans=0;
for(ll i=n-k,f=1;i>=m;i--,f=-f)
ans=(ans+f*F[p].a[i]*fac[i]%P*C(n-i,k)%P)%P;
printf("%lld\n",(ans+P)%P);
return 0;
}

Loj#6503-「雅礼集训 2018 Day4」Magic【分治NTT】的更多相关文章

  1. Loj #6503. 「雅礼集训 2018 Day4」Magic

    Loj #6503. 「雅礼集训 2018 Day4」Magic 题目描述 前进!前进!不择手段地前进!--托马斯 · 维德 魔法纪元元年. 1453 年 5 月 3 日 16 时,高维碎片接触地球. ...

  2. LOJ#6503.「雅礼集训 2018 Day4」Magic[容斥+NTT+启发式合并]

    题意 \(n\) 张卡牌 \(m\) 种颜色,询问有多少种本质不同的序列满足相邻颜色相同的位置数量等于 \(k\). 分析 首先本质不同不好直接处理,可以将同种颜色的卡牌看作是不相同的,求出答案后除以 ...

  3. 【loj#6503.】「雅礼集训 2018 Day4」Magic(生成函数+容斥)

    题面 传送门 题解 复杂度比较迷啊-- 以下以\(n\)表示颜色总数,\(m\)表示总的卡牌数 严格\(k\)对比较难算,我们考虑容斥 首先有\(i\)对就代表整个序列被分成了\(m-i\)块互不相同 ...

  4. LOJ6503. 「雅礼集训 2018 Day4」Magic(容斥原理+NTT)

    题目链接 https://loj.ac/problem/6503 题解 题中要求本质不同的序列数量,不太好搞.我们考虑给相同颜色的牌加上编号,这样所有牌都不相同.那么如果我们求出了答案,只需要将答案除 ...

  5. 2018.10.27 loj#6035. 「雅礼集训 2017 Day4」洗衣服(贪心+堆)

    传送门 显然的贪心题啊...考试没调出来10pts滚了妙的一啊 直接分别用堆贪心出洗完第iii件衣服需要的最少时间和晾完第iii件衣服需要的最少时间. 我们设第一个算出来的数组是aaa,第二个是bbb ...

  6. LOJ #6509. 「雅礼集训 2018 Day7」C

    神仙题 LOJ #6509 题意 给定一棵树,点权为0/1,每次随机一个点(可能和之前所在点相同)走到该点并将其点权异或上1 求期望的移动距离使得所有点点权相同 题解 根本不会解方程 容易发现如果一个 ...

  7. loj 6037 「雅礼集训 2017 Day4」猜数列 - 动态规划

    题目传送门 传送门 题目大意 有一个位置数列,给定$n$条线索,每条线索从某一个位置开始,一直向左或者向右走,每遇到一个还没有在线索中出现的数就将它加入线索,问最小的可能的数列长度. 依次从左到右考虑 ...

  8. Loj 6036 「雅礼集训 2017 Day4」编码 - 2-sat

    题目传送门 唯一的传送门 题目大意 给定$n$个串,每个串只包含 ' .问是否可能任意两个不同的串不满足一个是另一个的前缀. 2-sat的是显然的. 枚举每个通配符填0还是1,然后插入Trie树. 对 ...

  9. LOJ #6037.「雅礼集训 2017 Day4」猜数列 状压dp

    这个题的搜索可以打到48分…… #include <cstdio> #include <cstring> #include <algorithm> ; bool m ...

随机推荐

  1. 通俗易懂理解——dijkstra算法求最短路径

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径.它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止 ###基本思想 通过Dij ...

  2. 【springcloud】一文带你搞懂API网关

    作者:aCoder2013 https://github.com/aCoder2013/blog/issues/35 前言 假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员.商品 ...

  3. ffmpeg细节整理记录

    ffmpeg细节整理记录 1.-vcodec.-code:v.-c:v ffmpeg的官方文档 -vcodec 是 -code:v 别名. -vcodec codec (output) Set the ...

  4. 🏆【JVM技术专区】「难点-核心-遗漏」TLAB内存分配+锁的碰撞(技术串烧)!

    JVM内存分配及申请过程 当使用new关键字或者其他任何方式进行创建一个类的对象时,JVM虚拟机需要为该对象分配内存空间,而对象的大小在类加载完成后已经确定了,所以分配内存只需要在Java堆中划分出一 ...

  5. SpringBoot应用中使用AOP记录接口访问日志

    SpringBoot应用中使用AOP记录接口访问日志 本文主要讲述AOP在mall项目中的应用,通过在controller层建一个切面来实现接口访问的统一日志记录. AOP AOP为Aspect Or ...

  6. Session原理、生命周期及购物车功能的实现

    在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据(保存该浏览器(会话)的相关信息)时 ...

  7. 如何在RHEL7或CentOS 7系统下修改网卡名称(亲测有效~!)

    亲测有效的更改RHEL7或CentOS 7的网卡名称的方法, 按照以下4步来操作就可以实现! Step 1 :网卡配置文件名称重命名为eth0[root@localhost ~]# ifconfige ...

  8. Git - Mac 电脑使用 brew 更新 Git

    安装 Homebrew Homebrew 是一个软件包管理器.它的作用就是将软件包安装到自己的目录中,然后将其文件符号链接到 /usr/local.更多信息,请自行进入官网查看 https://bre ...

  9. Toast控件

    ------------恢复内容开始------------ 四种常见的App弹窗设计:Toast.Dialog.Actionbar 和 Snackbar 弹窗又称为对话框,是App与用户进行交互的常 ...

  10. Python中正则表达式简介

    目录 一.什么是正则表达式 二.正则表达式的基础知识 1. 原子 1)普通字符作为原子 2)非打印字符作为原子 3) 通用字符作为原子 4) 原子表 2. 元字符 1)任意匹配元字符 2)边界限制元字 ...