五一蹲大牢 叁
T1 数列 Sequence
题目描述
我们称一个长度为 2n 的数列是有趣的,当且仅当该数列满足以下三个条件:(1)它是从 1 到 2n 共 2n个整数的一个排列{ai};
(2)所有的奇数项满足 a1<a3<…<a2n-1,所有的偶数项满足 a2<a4<…<a2n;
(3)任意相邻的两项 a2i-1与 a2i(1≤i≤n)满足奇数项小于偶数项,即:a2i-1<a2i。 现在的任务是:对于给定的n,请求出有多少个不同的长度为 2n 的有趣的数列。因为 最后的答案可能很大,所以只要求输出答案 mod P 的值。
输入格式
输入文件只包含用空格隔开的两个整数 n 和 P。输出格式
仅含一个整数,表示不同的长度为 2n 的有趣的数列个数 mod P 的值。样例输入
3 6样例输出
5数据范围与约定
对于 30%的数据满足 n≤1000;对于另外 30%的数据满足 P 为质数;
对于100%的数据满足 n≤1000000 且 P≤1000000000。
洛谷题号P3200
把前几个n的答案用暴力打出来,发现是卡特兰常数,但是P不是质数,所以可能没有逆元,所以用式子求,求组合数时就
,考虑质因数分解n!,因为n!中有(n/p)+(n/p2)+(n/p3)+.....,所以实现起来很方便
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int pri[10000005],vis[10000005],tot=0;
int p;
int calc(int m,int x){
int o=x,res=0;
while(m>=o){
res+=m/o;
o*=x;
}
return res;
}
int kkk(int a,int b){
int res=1;
while(b){
if(b&1)res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
int C(int m,int n){
int res=1;
for(int i=1;pri[i]<=n;i++){
int x=calc(n,pri[i]),y=calc(m,pri[i]),z=calc(n-m,pri[i]);
x-=z+y;
res=res*kkk(pri[i],x)%p;
}
return res;
}
int n;
signed main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%I64d%I64d",&n,&p);
for(int i=2;i<=10000000;i++){
if(!vis[i])vis[i]=i,pri[++tot]=i;
for(int j=1;j<=tot&&pri[j]*i<=10000000;j++){
vis[pri[j]*i]=pri[j];
if(i%pri[j]==0)break;
}
}
int x=C(n,2*n),y=C(n-1,2*n);
printf("%I64d",((x-y)%p+p)%p);
return 0;
}
T2 乘法 Mul
题目描述输入一个 n ∗ n 的矩阵 A,请求出对 m 取模的结果。
输入格式
第一行为三个正整数 n, k, m,含义如上所述;
接下来 n 行,每行输入 n 个非负整数,用于描述矩阵 A。
输出格式
输出 n 行,每行 n 个整数,表示答案矩阵。
样例输入
2 2 5
2 1
0 3
样例输出
1 1
0 2
数据范围与约定
对于前 30% 的数据,k<=30;
对于另外 30% 的数据,n=1;
对于 100%的数据,n<=30, k<=1e9 , m<=1e4,输入矩阵的数在 0~m-1 范围内
令s[i]=a1+a2+a3+...+a2^i,由于s[i]=s[i-1]+a2^(i-1),所以可以在快速幂时递推求出s[i],由于任意一个数字可以用二进制表示,如6=(110)2,及ans=s[3]+s[2]*a2^(3),所以可以把k拆成长度为2的整数次方的小段,统计每一小段即可。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int m;
int n,k;
struct node{
int a[35][35];
void clear(){
memset(a,0,sizeof(a));
}
node operator +(node x){
node c;
c.clear();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
c.a[i][j]=a[i][j]+x.a[i][j];
c.a[i][j]%=m;
}
}
return c;
}
node operator *(node x){
node c;
c.clear();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int res=0;
for(int k=1;k<=n;k++)res=res+a[i][k]*x.a[k][j]%m;
c.a[i][j]=res%m;
}
}
return c;
}
void print(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%I64d ",a[i][j]);
}
puts("");
}
}
}s[55],pw[55];
node o,ans;
signed main(){
freopen("mul.in","r",stdin);
freopen("mul.out","w",stdout);
scanf("%I64d%I64d%I64d",&n,&k,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%I64d",&o.a[i][j]);
}
}
int b=k,tot=1;
ans.clear();
s[0]=o;
tot=0;
while(b){
s[tot+1]=o*s[tot]+s[tot];
/* s[tot].print();*/
pw[tot]=o;
o=o*o;
tot++;
b>>=1;
}
pw[tot]=o;
o.clear();
for(int i=1;i<=n;i++)o.a[i][i]=1;
for(int i=31;i>=0;i--){
if(k&(1<<i)){
ans=ans+o*s[i];
/*ans.print();
puts("-------------------");*/
o=o*pw[i];
}
}
ans.print();
return 0;
}
T3 生物 Creature
题目描述
在一个无限长的一维空间中,存在着一个奇特的生物,它的身体上顺次有着 n + 1 个 刻印,每个刻印可以用一个正整数来表示。已知它最后一个刻印的值为 m,而其它 n 个刻 印的值均不超过 m,并且两个刻印的值可以相同。 这个生物每次可以选中它的任意一个刻印,并且按照这个刻印的值 k,选择向它所在位 置的前或后闪烁 k 个单位。我们称可以使得这个生物能够通过若干次闪烁,到达一维空间 任何一个位置的刻印序列为超刻印序列。 现在刻印序列显然一共有 m n 种,为了研究这个生物,请你求出其中超刻印序列的数 目。
输入格式
仅一行两个整数,分别为 n, m。
输出格式
输出一行一个整数,表示超刻印序列的数目对 1e9 +7 取模的结果。
样例输入
2 4
样例输出
12
数据范围与约定
对于前 20%的数据,保证 n,m <= 5;
对于 100%的数据,保证 1<=n<=15,1<=m<=1e8。
这道题就是要找满足gcd等于1的序列数,证明见裴蜀定理。正难则反,用总数减去不满足的,因为最后一个为m,先分解m的质因数,若数列每个数都存在m的某个质因数那么就不满足,而1~m中满足此条件的有m/“该质数”个,由于有n个位置,所以对于每个质数有(m/“该质数”)n种,但是考虑这样会算重,如6会被2给算一次,又会被3算一次,所以考虑用容斥原理来消除重复的部分。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int pri[100005],vis[100005],tot=0;
int p[20],cnt=0,ans=0,mod=1e9+7,s[20],tp=0;
int kkk(int a,int b){
int res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void dfs(int k,int lst,int S,int op){
if(k==S+1){
int res=1;
for(int i=1;i<=S;i++)res*=s[i];
ans=((ans+kkk(m/res,n)*op)%mod+mod)%mod;
return;
}
for(int i=lst;i<=cnt;i++){
s[++tp]=p[i];
dfs(k+1,i+1,S,op);
--tp;
}
}
signed main(){
freopen("creature.in","r",stdin);
freopen("creature.out","w",stdout);
for(int i=2;i<=100000;i++){
if(!vis[i])vis[i]=i,pri[++tot]=i;
for(int j=1;j<=tot&&pri[j]*i<=100000;j++){
vis[pri[j]*i]=pri[j];
if(i%pri[j]==0)break;
}
}
scanf("%I64d%I64d",&n,&m);
int o=m;
for(int i=1;i<=tot&&pri[i]<=o;i++){
if(o%pri[i]==0){
p[++cnt]=pri[i];
while(o%pri[i]==0)o/=pri[i];
}
}
if(o!=1)p[++cnt]=o;
for(int i=1,op=1;i<=cnt;i++,op*=-1){
tp=0;
dfs(1,1,i,op);
}
printf("%I64d",((kkk(m,n)-ans)%mod+mod)%mod);
return 0;
}
------2022.5.6

浙公网安备 33010602011771号