BestCoder Round #85题解

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(n4logn2)

代码: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(xa1,xb1)(1a,bn)(1x,n1000000)

思路:首先等式 gcd(xa1,xb1)=xgcd(a,b)1 成立令 f[n]=1+...+xn1=(1xn)/(1x) ,

而(1-x^p)%(1-x^q)==0当前仅当p%q==0

所以我们要求的便是 xgcd(a,b)nn ,

考虑枚举 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)

你可能感兴趣的:(bestcoder)