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 ...
随机推荐
- Java Volatile关键字(转)
出处: Java Volatile关键字 Java的volatile关键字用于标记一个变量“应当存储在主存”.更确切地说,每次读取volatile变量,都应该从主存读取,而不是从CPU缓存读取.每次 ...
- T100弹出是否确认窗体方式
例如: IF NOT cl_ask_confirm('aim-00108') THEN CALL s_transaction_end(') CALL cl_err_collect_show() RET ...
- Validator自动验证与手动验证
自动: public JResult projectAdd(@Valid Project project, BindingResult result) {Map<String,Object> ...
- Python(六) —— 网络请求
接口调用 接口调用有几个模块可以用:urllib 和 requests ,urllib 是内置的模块,极其不好用,强烈推荐用 requests 模块 get 请求 1.普通的 get 请求 impor ...
- 将磁盘从FAT格式转换为NTFS格式的方法
不需要进行格式化,只需在命令提示符中输入如下内容:CONVERT X:/FS:NTFS把X换成你需要的盘符,转一个盘需十几或几十秒不等..注意:此方法不可逆转,FAT32转到NTFS后不可转回,当然也 ...
- MVC4学习要点记三
一.数据迁移用来解决code first情况下当增加.删除.改变实体类,或改变DbContext类后,相应地更新数据库结构而不会对现有数据产生影响. 1.启用迁移指令:enable-migration ...
- C语言无法使用引用,一定要使用怎么办? ------指针的指针做参数
#include <stdio.h> #include <stdlib.h> #include <string.h> void fun1(char** s); vo ...
- vuex用法
// 配置vuex的步骤 // 1. 运行 cnpm i vuex -S // 2. 导入包 import Vuex from 'vuex' // 3. 注册vuex到vue中 Vue.use(Vue ...
- webpack初体验_集成插件_集成loader
webpack初体验 如果没装 webpack 就先装一下,命令行输入npm i webpack -g 新建一个项目 创建一个空的项目 定义一个名称 创建一个Module 选择静态 web 输入名称 ...
- Linux基础命令01
绝对路径:从“/”根目录下开始 常用的一些命令选项 ls ls :使用方式 ls {空格}选项 {空格}参数 (全写为list)(等同于dir) 列出当前目录下所有的文件,包括隐藏文件 ls -a ...