A.传送门:Hdu 5776 sum
题意:给定一个数列,求是否存在连续子列和为m的倍数,存在输出YES,否则输出NO(1<=n<=100000,1<=m<=5000)
思路:根据有无两个前缀和对m取模相同的数或者有无前缀和对m取模为0进行判断。
代码:Hdu 5776 ac代码
B.传送门:Hdu 5777 domino
题意:小白在玩一个游戏。桌子上有n张多米诺骨牌排成一列。它有k次机会,每次可以选一个还没有倒的骨牌,向左或者向右推倒。每个骨牌倒下的时候,若碰到了未倒下的骨牌,可以把它推倒。小白现在可以随意设置骨牌的高度,但是骨牌高度为整数,且至少为1,并且小白希望在能够推倒所有骨牌的前提下,使所有骨牌高度的和最小。
思路:先将每个骨牌的初始长度记为1,如果推倒这个骨牌的时候要连带下一个骨牌也被推倒的话,那么这个骨牌的高度将加上和下一个骨牌之间的距离。
现在假设所有骨牌推倒的时候都需要推倒下一个骨牌,所以现在总的骨牌长度为每两个之间的间距+n,又因为有k次免费推得,前面又使用一次免费推得,所以减去间距最大的k-1个间距便可以了。
代码:Hdu 5777 ac代码
C.传送门:Hdu 5778 abs
题意:给定一个数x,求正整数y>=2,使得满足以下条件:
1.y-x的绝对值最小
2.y的质因数分解式中每个质因数均恰好出现2次。
思路:由于y质因数分解式中每个质因数均出现2次,那么y是一个完全平方数,设y=z*z,题目可转换成求z,使得每个质因数出现1次. 我们可以暴力枚举z,检查z是否符合要求,显然当z是质数是符合要求,由素数定理可以得,z的枚举量在logn级别,所以总的时间复杂度为 O(n−−√4logn−−√2)
代码:Hdu 5778 ac代码
D.传送门:Hdu 5779 Tower Defence
题意:小白最近痴迷于玩Tower Defence。他想要自己制作一张地图。地图是一张有n个点的无向图(图可以不连通,没有重边和自环),所有边的长度都为1,满足从1号点到其他任意一个点的最短路都不等于k.小白想知道这样的图有多少个。如果两个顶点不连通,那么它们之间的距离为无穷大。(1<=n,k<=60)
思路:要求最短路不等于k->与1相连的最短路<=k-1
dp[i][d][j]表示当前的连通块的大小为i,最短路为d,恰好离1的距离为d的点有j个,
要计算dp[i][d][j]通过枚举dp[i-j][d-1][k]中的k,然后统计方案数
d与d-1两层之间的连边方案数(2^k-1)^j,d层之间的点连边的方案数(j-1)!
d层选择的j个点的方案数C[i-1]j
dp[i][d][j]对答案的贡献,乘以从n-1个点中选择i-1个点的方案数,另外n-i个点的任意组合(n-i)!
#include
using namespace std;
long long dp[61][61][61];
const int MOD=1000000007;
long long pre[61],Pow[61],PPow[61][61];
int C[61][61];
int main(){
int _,n,k;
C[0][0]=1;
for(int i=1;i<=60;i++){
C[i][0]=1,C[i][i]=1;
for(int j=1;j1][j]+C[i-1][j-1])%MOD;
}
Pow[0]=1,pre[0]=1;
for(int i=1;i<=60;i++)
Pow[i]=Pow[i-1]*2%MOD,pre[i]=pre[i-1]*Pow[i-1]%MOD;
for(int i=1;i<=60;i++){ //预处理(2^k-1)^j
int num=Pow[i]-1;
PPow[i][0]=1;
for(int j=1;j<=60;j++)
PPow[i][j]=PPow[i][j-1]*num%MOD;
}
scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&k);
memset(dp,0,sizeof(dp));
dp[1][0][1]=1;
long long ans=pre[n-1];
for(int i=2;i<=n;i++) //枚举联通块的大小
for(int d=1;d<=min(k-1,i);d++) //枚举最大距离
for(int j=1;j<=i-d;j++){ //距离为d的有几个点
for(int x=1;x<=i-j-(d-1);x++) //枚举距离为d-1的点有多少个
dp[i][d][j]=(dp[i][d][j]+dp[i-j][d-1][x]*PPow[x][j]%MOD*C[i-1][j]%MOD*pre[j]%MOD)%MOD;
ans=(ans+dp[i][d][j]*C[n-1][i-1]%MOD*pre[n-i]%MOD)%MOD;
//printf("dp[%d][%d][%d] %lld %lld\n",i,d,j,dp[i][d][j],dp[i][d][j]*C[n-1][i-1]%MOD*pre[n-i]%MOD%MOD);
}
printf("%lld\n",ans);
}
return 0;
}
E.传送门:Hdu 5780 gcd
题意:小白学会了求最大公约数,于是打算解决一个问题:给定x,n
求 ∑gcd(xa−1,xb−1)(1≤a,b≤n)(1≤x,n≤1000000)
思路:首先等式 ∑gcd(xa−1,xb−1)=xgcd(a,b)−1 成立令 f[n]=1+...+xn−1=(1−xn)/(1−x) ,
而(1-x^p)%(1-x^q)==0当前仅当p%q==0
所以我们要求的便是 ∑∑xgcd(a,b)−n∗n ,
考虑枚举 gcd(a,b) ,我们对a进行考虑,a可以取
gcd(a,b)∗1,gcd(a,b)∗2,gcd(a,b)∗3,...,gcd(a,b)∗(n/gcd(a,b))
而当a的值确定了之后,假设为i,那么b能取得值的种类便为phi[i]
同理对b进行考虑也是同样的道理
所以当gcd为i时,总共的答案数便为2*(phi[1]+phi[2]+…+phi[n/i])-1
有因为n/i的值不超过srqt(n)个,我们可以根据n/i分段,相同的一段x^d-1用等比数列进行求解
#include
using namespace std;
const int MAXN=1000000;
const int MOD=1000000007;
bool check[MAXN+10];
int phi[MAXN+10],prime[MAXN+10],tot,sum[MAXN+10];
long long inv[MAXN+10];
void phi_and_prime_table(int N){
memset(check,false,sizeof(check));
phi[1]=1;
tot=0;
for(int i=2;i<=N;i++){
if(!check[i]){
prime[tot++]=i;
phi[i]=i-1;
}
for(int j=0;jif(i*prime[j]>N) break;
check[i*prime[j]]=true;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
for(int i=1;i<=N;i++)
sum[i]=(sum[i-1]+phi[i])%MOD;
}
int Pow(long long x,int n){
long long ans=1;
while(n){
if(n&1)
ans=ans*x%MOD;
n>>=1;
x=x*x%MOD;
}
return ans;
}
void solve(long long x,int n){
long long ans=0;
for(int i=1,la=0;i<=n;i=la+1){ //枚举gcd
la=n/(n/i);
ans+=1LL*(2*sum[n/i]-1)*((Pow(x,la+1)-Pow(x,i))%MOD+MOD)%MOD*inv[x-1]%MOD;
}
printf("%lld\n",((ans-1LL*n*n)%MOD+MOD)%MOD);
}
int main(){
int _,n;
long long x;
phi_and_prime_table(MAXN);
inv[1] = 1;
for (int i = 2; i<=MAXN; i++)
inv[i] = inv[MOD%i]*(MOD-MOD/i)%MOD;
scanf("%d",&_);
while(_--){
scanf("%lld%d",&x,&n);
if(x==1){
printf("0\n");
continue;
}
solve(x,n);
}
return 0;
}
思维拓展:如果a的范围为1<=a<=n,1<=b<=m->先对d进行分段,再对gcd(i,j)==1进行分段求解(1<=i<=n/d,1<=j<=m/d)