题意

\(n\) 张卡牌 \(m\) 种颜色,询问有多少种本质不同的序列满足相邻颜色相同的位置数量等于 \(k\)。

分析

首先本质不同不好直接处理,可以将同种颜色的卡牌看作是不相同的,求出答案后除以 \(\prod {a_i!}\) 即可。

如果我们能够得到一个至少存在 \(k\) 个魔术对的排列数,就可以容斥了。

考虑单独处理每种颜色, 枚举一个颜色 \(i\),计算这种颜色至少有 \(j\) 对的方案总数。

可以选择 \(j\) 张牌保证这些牌一定跟在某张牌的后面,这样就可以形成 \(\geq j\) 个满足要求的魔术对了。

然后做一个背包。

定义状态 \(f_{i,j}\) 表示处理了前 \(i\) 种颜色,至少存在 \(j\) 对魔术对的排列数。

对于 \(f_{m,j}\),剩下的 \(n-j\) 张牌可以任意排列。背包是一个卷积的形式可以 NTT 优化。

合并背包的过程可以用堆维护,每次合并堆顶最小的两个背包数组,复杂度类似启发式合并。

所以总时间复杂度为 \(O(n\log^2n)\)。

技巧:在计算方案出现困难的时候考虑容斥简化条件(类似反演题目的思想)

代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
typedef pair<int,int> pii;
#define mp make_pair
inline int gi(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=1e5 + 7,mod=998244353;
int n,m,k,K,len,lst,sz[N<<1],a[N];
LL invfac[N],inv[N],fac[N];
vector<LL>v[N<<1];
priority_queue<pii,vector<pii>,greater<pii> >Q;
void add(LL &a,LL b){a+=b;if(a>=mod) a-=mod;}
LL Pow(LL a,LL b){
LL res=1ll;
for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod;
return res;
}
LL Inv(LL a){return Pow(a,mod-2);}
LL C(int n,int m){
return fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
void ntt(vector<LL> &c,int n,int f){
for(int i=0,j=0;i<n;++i){
if(i<j) swap(c[i],c[j]);
for(int k=n>>1;(j^=k)<k;k>>=1);
}
for(int i=1;i<n;i<<=1){
LL wn=Pow(3,(mod-1)/(i<<1));
if(f==-1) wn=Inv(wn);
for(int j=0;j<n;j+=i<<1){
LL w=1;
for(int k=0;k<i;++k,w=w*wn%mod){
LL x=c[j+k],y=w*c[j+k+i]%mod;
c[j+k]=(x+y)%mod;
c[j+k+i]=(x-y+mod)%mod;
}
}
}
if(f==-1){LL inv=Inv(n);for(int i=0;i<n;++i) c[i]=c[i]*inv%mod;}
}
int main(){
m=gi(),n=gi(),K=gi();
fac[0]=invfac[0]=inv[1]=1;
rep(i,1,n){
if(i^1) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
fac[i]=fac[i-1]*i%mod;
invfac[i]=invfac[i-1]*inv[i]%mod;
}
rep(c,1,m){
sz[c]=a[c]=gi();int x=a[c];
Q.push(mp(sz[c],c));
v[c].resize(x+7);
rep(i,0,x-1) v[c][i]=C(x,i)*fac[x-1]%mod*invfac[x-i-1]%mod;
}
while(!Q.empty()){
int x=Q.top().second;Q.pop();
if(Q.empty()) { lst=x; break; }
int y=Q.top().second;Q.pop();
for(len=1;len<=sz[x]+sz[y];len<<=1);
v[x].resize(len+7);
v[y].resize(len+7);
ntt(v[x],len,1);
ntt(v[y],len,1);
for(int i=0;i<len;++i) v[x][i]=v[x][i]*v[y][i]%mod;
ntt(v[x],len,-1); sz[x]+=sz[y];
v[y].clear();
Q.push(mp(sz[x],x));
}
LL ans=0;
rep(i,K,n-1)
add(ans,(i-K&1?mod-1:1)*v[lst][i]%mod*C(i,K)%mod*fac[n-i]%mod);
rep(i,1,m) ans=ans*invfac[a[i]]%mod;
printf("%lld\n",ans);
return 0;
}

LOJ#6503.「雅礼集训 2018 Day4」Magic[容斥+NTT+启发式合并]的更多相关文章

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

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

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

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

  3. Loj#6503-「雅礼集训 2018 Day4」Magic【分治NTT】

    正题 题目链接:https://loj.ac/p/6503 题目大意 \(n\)张卡\(m\)种,第\(i\)种卡有\(a_i\)张,求所有排列中有\(k\)对相邻且相同的卡牌. \(1\leq n\ ...

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

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

  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. LeetCode题解之 Binary Tree Preorder Traversal

    1.题目描述 2.问题分析 利用递归. 3.代码 vector<int> preorderTraversal(TreeNode* root) { vector<int> v; ...

  2. 互联网,IT,大数据,机器学习,AI知识tag云

    互联网基础: tcp/ip网络,linux运维,DNS,ipv6 web前端: javascript, es6, 组件化开发, vuejs, angularjs, react html5, css3, ...

  3. 跨过Django的坑

    在最近的Django的学习中,慢慢的开始踩坑,开此栏,专为收纳Django的坑,在以后的学习中以便警示.(使用工具为pycharm专业版2018.2.4,python3.5.2,Django版本2.1 ...

  4. python基础学习4----元组

    元组又叫只读列表,不可以修改其内容 1.创建元组 tuple1=(1,3,2,4,5) tuple2=()#空元组 tuple3=('a',) #元组中只有一个元素时要在元素后面加上逗号,否则会被当成 ...

  5. COM动态添加删除成员,类似JavaScript中调用的对象

    在JavaScript中调用对象时,可动态添加删除成员如: var obj=new Object; obj.member1='aaaaa'; obj.fun1=function() { alert(' ...

  6. Xmind在win10更改用户设置后出现Invalid Configuration Location 错误的解决办法

    错误原因: 因为一开始新建win10用户时,使用的是中文用户名,导致了部分软件比如IDEA读取C盘中配置文件时报错.我用管理员权限修改用户姓名为英文后,IDEA的问题虽然已经解决,但Xmind却报出了 ...

  7. Django商城项目笔记No.17用户部分-用户中心用户地址管理

    收货地址管理 首先就是新增地址 看图分析所需要保存的字段 因为是用户的地址,所以在users应用中的models创建模型类 class Address(BaseModel): "" ...

  8. mac brew安装mysql

    mac不自带mysql,这里需要重新安装,方法依然很简单 brew install mysql unset TMPDIR mysql_install_db --verbose --user=`whoa ...

  9. 8.3Solr API使用(StatsComponent聚合统计)

    转载请出自出处:http://eksliang.iteye.com/blog/2169134 一.概述 Solr可以利用StatsComponent 实现数据库的聚合统计查询,也就是min.max.a ...

  10. 水壶-[Kruskal重构树] [解题报告]

    水壶 本来从不写针对某题的题解,但因为自己实在是太蠢了,这道题也神TM的恶心,于是就写篇博客纪念一下 H水壶 时间限制 : 50000 MS 空间限制 : 565536 KB 评测说明 : 2s,51 ...