hdu 6088 Rikka with Rock-paper-scissors (2017 多校第五场 1004) 【组合数学 + 数论 + 模意义下的FFT】
首先利用组合数学知识,枚举两人的总胜场数容易得到

这还不是卷积的形式,直接搞的话复杂度大概是O(n^2)的,肯定会TLE。但似乎和卷积有点像?想半天没想出来。。多谢Q巨提醒,才知道可以用下面这个公式进行转化

最后,化得的公式为

另外注意,上式右边是一个卷积的形式,但是,所得和的第一项是不需要加上的(不过图中公式没有体现)。结合实际意义大概就是,i==0&&j==0时,gcd(i,j)不存在约数d,虽然0可以被任意正整数整除 & 第一项不为0
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define upmo(a,b) (((a)=((a)+(b))%mod)<0?(a)+=mod:(a)) // 相加后取模 int n,mod; namespace FFT_MO //前面需要有 mod(1e8~1e9级别),upmo(a,b) 的定义
{
const int FFT_MAXN=<<;
const db pi=.14159265358979323846264338327950288L;
struct cp
{
db a,b;
cp(double a_=,double b_=)
{
a=a_,b=b_;
}
cp operator +(const cp&rhs)const
{
return cp(a+rhs.a,b+rhs.b);
}
cp operator -(const cp&rhs)const
{
return cp(a-rhs.a,b-rhs.b);
}
cp operator *(const cp&rhs)const
{
return cp(a*rhs.a-b*rhs.b,a*rhs.b+b*rhs.a);
}
cp operator !()const
{
return cp(a,-b);
}
}nw[FFT_MAXN+],f[FFT_MAXN],g[FFT_MAXN],t[FFT_MAXN]; //a<->f,b<->g,t<~>c
int bitrev[FFT_MAXN]; void fft_init() //初始化 nw[],bitrev[]
{
int L=;while((<<L)!=FFT_MAXN) L++;
for(int i=;i<FFT_MAXN;i++) bitrev[i]=bitrev[i>>]>>|((i&)<<(L-));
for(int i=;i<=FFT_MAXN;i++) nw[i]=cp((db)cosl(*pi/FFT_MAXN*i),(db)sinl(*pi/FFT_MAXN*i));
} // n已保证是2的整数次幂
// flag=1:DFT | flag=-1: IDFT
void dft(cp *a,int n,int flag=)
{
int d=;while((<<d)*n!=FFT_MAXN) d++;
for(int i=;i<n;i++) if(i<(bitrev[i]>>d))
swap(a[i],a[bitrev[i]>>d]); // NOTICE!
for(int l=;l<=n;l<<=)
{
int del=FFT_MAXN/l*flag; // 决定 wn是在复平面是顺时针还是逆时针变化,以及变化间距
for(int i=;i<n;i+=l) // ?????????????????
{
cp *le=a+i,*ri=a+i+(l>>); // ?????????????????
cp *w=flag==? nw:nw+FFT_MAXN; // 确定wn的起点
for(int k=;k<(l>>);k++)
{
cp ne=*ri * *w;
*ri=*le-ne,*le=*le+ne;
le++,ri++,w+=del;
}
}
}
if(flag!=) for(int i=;i<n;i++) a[i].a/=n,a[i].b/=n;
} // convo(a,n,b,m,c) a[0..n]*b[0..m] -> c[0..n+m]
void convo(LL *a,int n,LL *b,int m,LL *c)
{
for(int i=;i<=n+m;i++) c[i]=;
int N=;while(N<=n+m) N<<=; // N是c扩展后的长度
for(int i=;i<N;i++) //扩展 a[],b[] ,存入f[],g[],注意取模
{
LL aa=i<=n?a[i]:,bb=i<=m? b[i]:;
aa%=mod,bb%=mod;
f[i]=cp(db(aa>>),db(aa&));
g[i]=cp(db(bb>>),db(bb&));
}
dft(f,N),dft(g,N);
for(int i=;i<N;i++) // 频域求积 // ?????????????????
{
int j=i? N-i:;
t[i]=((f[i]+!f[j])*(!g[j]-g[i])+(!f[j]-f[i])*(g[i]+!g[j]))*cp(,0.25);
}
dft(t,N,-);
for(int i=;i<=n+m;i++) upmo(c[i],(LL(t[i].a+0.5))%mod<<);
for(int i=;i<N;i++) // 频域求积 // ?????????????????
{
int j=i? N-i:;
t[i]=(!f[j]-f[i])*(!g[j]-g[i])*cp(-0.25,)+cp(,0.25)*(f[i]+!f[j])*(g[i]+!g[j]);
}
dft(t,N,-);
for(int i=;i<=n+m;i++) upmo(c[i],LL(t[i].a+0.5)+(LL(t[i].b+0.5)%mod<<));
}
} //==============预处理阶乘及阶乘逆元============== LL qpow(LL x,LL n) //求x^n%mod
{
LL ret=;
for(; n; n>>=)
{
if(n&) ret=ret*x%mod;
x=x*x%mod;
}
return ret;
}
LL inv(LL x)
{
return qpow(x,mod-);
}
const LL M=1e5+;
LL fac[M+]; //阶乘
LL inv_of_fac[M+]; //阶乘的逆元
void init_fac()
{
fac[]=;
for(int i=; i<=M; i++)
fac[i]=fac[i-]*i%mod;
inv_of_fac[M]=qpow(fac[M],mod-);
for(int i=M-; i>=; i--)
inv_of_fac[i]=inv_of_fac[i+]*(i+)%mod;
}
//================================================ //===================phi(x)打表=================== const int maxn=;
int phi[maxn+];
void init_phi()
{
memset(phi,,sizeof(phi)); //初始化为0
phi[]=;
for(int i=; i<=maxn; i++)
{
if(!phi[i]) //当i是质数时
for(int j=i; j<=maxn; j+=i) //筛选所有因子为i的数
{
if(!phi[j]) phi[j]=j; //若未赋值过,先初始化
phi[j]=phi[j]/i*(i-); //i是质因数(1-1/i)=(i-1)/i,先除再乘是为了防止越界。
}
}
}
//================================================ LL a[<<|],b[<<|],c[<<|]; int main()
{
init_phi();FFT_MO::fft_init(); //=============debug============== // int n,m;
// mod=1e9+7;
// while(cin>>n>>m)
// {
// for(int i=0;i<=n;i++) cin>>a[i];
// for(int i=0;i<=m;i++) cin>>b[i];
// FFT_MO::convo(a,n,b,m,c);
// for(int i=0;i<=n+m;i++)
// cout<<c[i]<<' ';
// puts("");
// } //================================
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&mod);
init_fac();
LL ans=;
for(int d=;d<=n;d++)
{
int N=n/d;
for(int i=;i<=N;i++) a[i]=b[i]=inv_of_fac[i*d];
FFT_MO::convo(a,N,b,N,c);
LL temp=;
for(int i=;i<=N;i++) temp=(temp+c[i]*inv_of_fac[n-i*d])%mod;
ans=(ans+temp*phi[d])%mod;
}
ans=ans*fac[n]%mod*qpow(,n)%mod;
printf("%lld\n",ans);
}
}
hdu 6088 Rikka with Rock-paper-scissors (2017 多校第五场 1004) 【组合数学 + 数论 + 模意义下的FFT】的更多相关文章
- hdu多校第五场1004 (hdu6627) equation 1 计算几何
题意: 给你一个C,再给你n组a,b,让你求x取什么值的时候,$ \sum_{i=1}^n |a_i*x+b_i| =C $,要求求出解的个数,并用最简分数从小到大表示,如果有无穷多解,输出-1. 题 ...
- HDU 6088 - Rikka with Rock-paper-scissors | 2017 Multi-University Training Contest 5
思路和任意模数FFT模板都来自 这里 看了一晚上那篇<再探快速傅里叶变换>还是懵得不行,可能水平还没到- - 只能先存个模板了,这题单模数NTT跑了5.9s,没敢写三模数NTT,可能姿势太 ...
- 2018 ACM-ICPC 中国大学生程序设计竞赛线上赛 H题 Rock Paper Scissors Lizard Spock.(FFT字符串匹配)
2018 ACM-ICPC 中国大学生程序设计竞赛线上赛:https://www.jisuanke.com/contest/1227 题目链接:https://nanti.jisuanke.com/t ...
- 2017多校第7场 HDU 6128 Inverse of sum 推公式或者二次剩余
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6128 题意:给你n个数,问你有多少对i,j,满足i<j,并且1/(ai+aj)=1/ai+1/a ...
- 2017多校第5场 HDU 6085 Rikka with Candies bitset
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6085 题意:存在两个长度为n,m的数组A,B.有q个询问,每个询问有一个数字k,可以得到Ai%Bj=k ...
- SDUT 3568 Rock Paper Scissors 状压统计
就是改成把一个字符串改成三进制状压,然后分成前5位,后5位统计, 然后直接统计 f[i][j][k]代表,后5局状压为k的,前5局比和j状态比输了5局的有多少个人 复杂度是O(T*30000*25*m ...
- HDU 6088 Rikka with Rock-paper-scissors(NTT+欧拉函数)
题意 \(n\) 局石头剪刀布,设每局的贡献为赢的次数与输的次数之 \(\gcd\) ,求期望贡献乘以 \(3^{2n}\) ,定义若 \(xy=0\) 则,\(\gcd(x,y)=x+y\) 思路 ...
- FFT(Rock Paper Scissors Gym - 101667H)
题目链接:https://vjudge.net/problem/Gym-101667H 题目大意:首先给你两个字符串,R代表石头,P代表布,S代表剪刀,第一个字符串代表第一个人每一次出的类型,第二个字 ...
- Gym - 101667H - Rock Paper Scissors FFT 求区间相同个数
Gym - 101667H:https://vjudge.net/problem/Gym-101667H 参考:https://blog.csdn.net/weixin_37517391/articl ...
随机推荐
- Archlinux开启ssh服务命令
Archlinux开启ssh服务命令: systemctl enable sshd.service 开机启动 systemctl start sshd.service 立即启动 systemctl r ...
- windows登录密码忘记了怎么办?
利用PE工具进行进行修改密码或者重置系统密码,正对于服务器也同样试用 目前U启动制作效果还不错,黑鲨一键装机,以及老毛桃我觉得还是算了,U深度也不错 经过这么久,小编也把该测试的测试了,,小编比较懒, ...
- Ubuntu 下几种软件安装的方法小结
1.tar.gz软件包的安装 1)解压tar.gz包 .tar.gz -C /home/Desktop # 将软件包名.tar.gz解压到指定的目录下 2)进入解压后的文件目录下 执行“./ ...
- HTML accessKey约定俗成的标准
accessKey属性提供了键盘快捷键 例: <a href="index.html" accessKey="1">Home</a> 这 ...
- 模仿ORM
ORM 对象关系映射 类 ---------->映射---------> 表 对象 ------>映射----------> 一条记录 对象点属性 --->映射 ...
- spark算子篇-repartition and coalesce
我们知道 RDD 是分区的,但有时候我们需要重新设置分区数量,增大还是减少需要结合实际场景,还有可以通过设置 RDD 分区数来指定生成的文件的数量 重新分区有两种方法:repartition and ...
- Docker——桥接网络配置
[root@localhost ~]# cd /etc/sysconfig/network-scripts/ [root@localhost network-scripts]# ls ifcfg-et ...
- PHP之配置
1) 错误日志 一.相关配置 需要将php.ini中的配置指令做如下修改: . error_reporting = E_ALL ;将会向PHP报告发生的每个错误 . display_errors = ...
- Html5+Css3小试牛刀
前因: 我开始做个收款系统,突然客户跑来要插进一个任务,据说他们老板挺在意的,一个小商场,一个首页,一个详情页,UI无自由发挥,要求,尽量好看点. 一番交谈后,确认这是一个对外的网站,最好移动端也能正 ...
- C# 获取当前执行DLL 所在路径
有的时候,当前执行的DLL 和启动的EXE 所在路径并不一致,这时我们想要获得当前执行DLL 所在路径可以使用下面的方法. // Summary: // Gets the path or UNC lo ...