前言
和上一次的区别就是,这周比赛难度稍微地降低
但是,还是太菜了被dalao吊打的恐惧
JZOJ 5791 阶乘
题目
给定
p
=
Π
i
=
1
n
a
[
i
]
p=\Pi_{i=1}^n a[i]
p=Πi=1na[i],若
p
×
q
=
m
!
,
q
∈
N
∗
p\times q=m!,q\in N*
p×q=m!,q∈N∗
求最小的
m
m
m
分析
然而这道题不算很难,首先把 p p p分解质因数,(当然不可能直接给 p p p),所以要把 a a a数组分解,那么之后枚举 p p p的质约数,并且判断若要求出 m m m,对于 p p p的质约数或它的倍数,最多得匹配到几才能满足 p p p的该质约数的指数,讲到这里,就讲完了
代码
#include <cstdio>
#define max(a,b) ((a)>(b))?(a):(b)
#define rr register
using namespace std;
int sum[100001];
inline signed in(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
signed main(){
while (1){
rr int n=in();
while (n--){
rr int x=in();
for (rr int j=2;j*j<=x;++j)//分解质因数
while (x%j==0) x/=j,sum[j]++;
if (x>1) sum[x]++;
}
break;
}
rr int ans=0;
for (rr int i=2;i<=100000;++i)
if (sum[i]>0){
rr int now=i;
while (sum[i]>0){
rr int tmp=now; now+=i;
while (tmp%i==0) sum[i]--,tmp/=i;//最多需要匹配到几才能满足
}
ans=max(ans,now-i);//加多了要减回去
}
return !printf("%d",ans);
}
JZOJ 5793 小S练跑步
最少拐弯问题加上限制无法走某个方向,广搜就可以过,
放出dalao的题解
没办法,我的代码还是得放上去(感性理解,在此不多赘述)
#include <cstdio>
#include <cstring>
#include <queue>
#define min(a,b) ((a)<(b))?(a):(b)
#define rr register
using namespace std;
struct node{int x,y; char dir;}; queue<node>q;
int dis[503][503][4],n,m; char could[503][503];
int main(){
freopen("run.in","r",stdin);
freopen("run.out","w",stdout);
scanf("%d%d",&n,&m);
memset(dis,127/3,sizeof(dis));
dis[1][1][0]=dis[1][1][1]=0;
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=m;++j){
could[i][j]=getchar();
while (could[i][j]!='U'&&could[i][j]!='L'&&could[i][j]!='D'&&could[i][j]!='S'&&could[i][j]!='R') could[i][j]=getchar();
}
if (could[1][1]=='S') return !printf("No Solution");
if (could[n][m]=='S') could[n][m]='R'; bool flag=0;
if (could[1][1]!='D') q.push((node){1,1,'D'});
if (could[1][1]!='R') q.push((node){1,1,'R'});
while (q.size()){
rr node t=q.front(); q.pop();
rr int xx=0,yy=0,ddir;
if (t.dir=='D'&&t.x<n&&could[t.x+1][t.y]!='S') xx=t.x+1,yy=t.y,ddir=0;
if (t.dir=='R'&&t.y<m&&could[t.x][t.y+1]!='S') xx=t.x,yy=t.y+1,ddir=1;
if (t.dir=='U'&&t.x>1&&could[t.x-1][t.y]!='S') xx=t.x-1,yy=t.y,ddir=2;
if (t.dir=='L'&&t.y>1&&could[t.x][t.y-1]!='S') xx=t.x,yy=t.y-1,ddir=3;
if (!xx||!yy) continue;
if (could[xx][yy]!='D'&&dis[xx][yy][0]>dis[t.x][t.y][ddir]+(t.dir!='D'&&(xx!=n||yy!=m)))
dis[xx][yy][0]=dis[t.x][t.y][ddir]+(t.dir!='D'&&(xx!=n||yy!=m)),q.push((node){xx,yy,'D'});
if (could[xx][yy]!='R'&&dis[xx][yy][1]>dis[t.x][t.y][ddir]+(t.dir!='R'&&(xx!=n||yy!=m)))
dis[xx][yy][1]=dis[t.x][t.y][ddir]+(t.dir!='R'&&(xx!=n||yy!=m)),q.push((node){xx,yy,'R'});
if (could[xx][yy]!='U'&&dis[xx][yy][2]>dis[t.x][t.y][ddir]+(t.dir!='U'&&(xx!=n||yy!=m)))
dis[xx][yy][2]=dis[t.x][t.y][ddir]+(t.dir!='U'&&(xx!=n||yy!=m)),q.push((node){xx,yy,'U'});
if (could[xx][yy]!='L'&&dis[xx][yy][3]>dis[t.x][t.y][ddir]+(t.dir!='L'&&(xx!=n||yy!=m)))
dis[xx][yy][3]=dis[t.x][t.y][ddir]+(t.dir!='L'&&(xx!=n||yy!=m)),q.push((node){xx,yy,'L'});
}
rr int ans=min(min(dis[n][m][0],dis[n][m][1]),min(dis[n][m][2],dis[n][m][3]));
if (ans<707406378) return !printf("%d",ans); else return !printf("No Solution");
}
JZOJ 5787 轨道
题目
v
=
a
1
×
a
2
×
⋯
×
a
n
k
v=\frac{a_1\times a_2\times\cdots\times a_n}{k}
v=ka1×a2×⋯×an
已知
n
n
n和
k
k
k,求这
n
n
n个正整数在都不大于
m
m
m的情况下有多少种选择方式,使得
g
c
d
(
v
,
k
)
=
1
gcd(v,k)=1
gcd(v,k)=1
分析
需要数论牵扯,然而这是道动态规划的题目,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为前
i
i
i个数,乘积与
k
k
k的最大公约数为
k
k
k的第
j
j
j个约数的方案数(且乘积除以公约数与
k
k
k互质)
容易得到
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
约
数
]
∗
d
p
[
1
]
[
j
/
约
数
]
dp[i][j]=dp[i-1][约数]*dp[1][j/约数]
dp[i][j]=dp[i−1][约数]∗dp[1][j/约数]
但是如何初始化是个问题,那么就是
m
/
a
[
i
]
x
=
1
g
c
d
(
x
,
k
)
=
1
的
个
数
。
\frac{m/a[i]}{x=1}gcd(x,k)=1的个数。
x=1m/a[i]gcd(x,k)=1的个数。,需要用容斥原理跑深搜,然而我不会莫比乌斯反演怎么做,于是
代码(80分TLE,请自行O2)
#include <cstdio>
#include <algorithm>
#include <vector>
#define answer(t) (dp[kdys2[j][t]][t1^1]*pre[rk[kdys[j]/kdys[kdys2[j][t]]]])//使代码简洁了许多
#define rr register//register的威力
using namespace std;
vector<int>kdys; vector<short>kdys2[3001],prime;//第一个小优化就是变成了short,虽然时间貌似多了点,但是内存比只用int少了一半
int m,k; short n,rk[10000001],dp[3001][2],t1=1,pre[3001];//没错,dp用滚动数组
void dfs(int lm,short dep,short o_n,int now,short &ans){
if (dep>prime.size()) ans=(ans+lm/now*o_n)%10007;
else dfs(lm,dep+1,o_n,now,ans),dfs(lm,dep+1,-o_n,now*prime[dep-1],ans);//容斥
}
signed main(){
scanf("%d%d%d",&n,&m,&k);
for (rr short i=1;i*i<=k;++i)//首先找出k的约数
if (k%i==0){
kdys.push_back(i);
if (i*i<k) kdys.push_back(k/i);
}
sort(kdys.begin(),kdys.end());
for (rr short i=2;i*i<=k&&k>1;++i)//然后找出k的质约数
if (k%i==0){
prime.push_back(i);
while (k%i==0) k/=i;
}
if (k>1) prime.push_back(k);
for (rr short i=0;i<kdys.size();++i){//找出k的约数的约数(必然也是k的约数)
rr char flag=65;
for (rr short j=0;j<=i;++j)
if (kdys[i]%kdys[j]==0)
flag=66,kdys2[i].push_back(j);
}
for (rr short i=0;i<kdys.size();++i){//初始化,记录编号
rk[kdys[i]]=i; rr short ans=0;
if (m/kdys[i]==0) continue;
dfs(m/kdys[i],1,1,1,ans);
dp[i][0]=pre[i]=ans%10007;
}
for (rr short i=2;i<=n;++i,t1^=1)
for (rr short j=0;j<kdys.size();++j){
rr short tot=0; if (!kdys2[j].size()) continue;//基本不可能,但是为了好看
for (rr short k=0;k<(kdys2[j].size()&15);++k) tot=(tot+answer(k))%10007;//循环展开
for (rr short k=(kdys2[j].size()&15);k<kdys2[j].size();k+=16){
tot=(tot+answer(k)+answer(k+1))%10007;
tot=(tot+answer(k+2)+answer(k+3))%10007;
tot=(tot+answer(k+4)+answer(k+5))%10007;
tot=(tot+answer(k+6)+answer(k+7))%10007;
tot=(tot+answer(k+8)+answer(k+9))%10007;
tot=(tot+answer(k+10)+answer(k+11))%10007;
tot=(tot+answer(k+12)+answer(k+13))%10007;
tot=(tot+answer(k+14)+answer(k+15))%10007;
}
//其实就是for (rr short k=0;k<kdys2[j].size();++k) tot=(tot+answer(k))%10007;
dp[j][t1]=tot;
}
return !printf("%d",(dp[kdys.size()-1][t1^1]%10007+10007)%10007);//输出答案
}
后续
沉迷OI,日渐消瘦虚胖