Bestcoder round#85 解题报告

本文提供了四道算法题目的详细解答过程,包括前缀和、贪心算法、完全平方数判断及动态规划等多种技巧的应用。每道题目均附带完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1001 sum

计算下前缀和,看有没有模m相同的即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
int pp[5100];
int T;
int flag;
int n,k;
int sum;
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        flag=0;
        memset(pp,0,sizeof(pp));
        pp[0]=1;
        scanf("%d %d",&n,&k);
        sum=0;
        for (int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            sum=(sum+x)%k;
            if (pp[sum]) flag=1;
            pp[sum]=1;
        }
        if (flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

1002 domino

贪心去掉k1个最大值即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;
int a[110000];
int T;
int flag;
int n,k;
LL ans;
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d %d",&n,&k);
        for (int i=1;i<n;i++)
            scanf("%d",&a[i]);
        sort(a+1,a+n);
        ans=0;
        for (int i=1;i<=max(n-k,0);i++)
            ans+=(LL)(a[i]+1LL);
        cout<<ans+min((LL)k,(LL)n)<<endl;
    }
    return 0;
}

1003 abs

因为y的每个质因子的次数都为2,所以y为完全平方数,然后暴力枚举下d(y=dd),判断d是否每个质因子都只出现了一次。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
#include<ctime>
using namespace std;
typedef long long LL;
LL x;
int T;
LL ans1,ans2;
int check(LL x)
{
    for (LL i=2;i*i<=x;i++)
    {
        if (x%i==0)
        {
            x/=i;
            if (x%i==0) return 0;
        }
    }
    return 1;
}
int main()
{
    cin>>T;
    while (T--)
    {   
        cin>>x;
        LL d=(LL)sqrt((double)x+0.5);
        for (LL i=1;;i++)
        {
            if (check(d+i)&&d+i>=2)
            {
                ans1=abs(x-(d+i)*(d+i));
                break;
            }
        }
        ans2=1e18; 
        for (LL i=0;;i++)
        {
            if ((d-i)<2) break;
            if (check(d-i))
            {
                ans2=abs(x-(d-i)*(d-i));
                break;
            }
        }
        cout<<min(ans1,ans2)<<endl;
    }
    return 0;
}

1004 Tower Defence

dp,f[i][j][k]i表示1号点所在的联通块的大小,j表示1号点与联通块内其他点的最大距离,k表示与1号点最短距离恰好为j的点的个数,f[i][j][k]表示这样的方案数。考虑其对答案的贡献,对于不在1号点所在联通块内的 其他点,可以随便连,方案数为2n(n1)2,将其乘起来即可。下面考虑f[i][j][k]的转移。枚举距离为j+1的点的个数l,首先用组合数从剩余n-i个点中选出l个点,这些点一定与第j层的点连至少一条边,所以层与层之间的连接方案为2k1,其次,这些点之间也可以互相连边,方案数为2l(l1)2。那么其对f[i+l][j+1][l]的贡献应该为

f[i][j][k]Clni(2k1)(2l(l1)2)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
#include<ctime>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
int n,k;
LL f[61][61][61];
LL ans;
LL pow_mod(LL a,LL b,LL p)
{
    LL tmp=1;
    a%=p;
    for(LL i=b;i;i>>=1,a=a*a%p)
        if(i&1)tmp=tmp*a%p;
    return tmp;
}
int T;
LL C[70][70];
int main()
{
    C[0][0]=1;
    for(int i=1;i<=61;i++){
        C[i][0]=1;
        for (int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
    scanf("%d",&T);
    while (T--)
    {   
        scanf("%d %d",&n,&k);
        memset(f,0,sizeof(f));
        f[1][0][1]=1;
        ans=0;
        for (int i=0;i<k;i++)
        {
            for (int j=1;j<=n;j++)
                for (int l=1;l<=j;l++)
                {
                    if (f[j][i][l]==0) continue;
                    ans=(ans+f[j][i][l]*pow_mod(2,(LL)(n-j)*(n-j-1)/2LL,mod))%mod;
                    for (int ll=1;ll<=n-j;ll++)
                    {
                        LL d=(((f[j][i][l]*pow_mod(pow_mod(2,l,mod)-1+mod,ll,mod))%mod)*pow_mod(2,(LL)(ll)*(ll-1)/2LL,mod))%mod;
                        d=(d*C[n-j][ll])%mod;
                        f[j+ll][i+1][ll]=(f[j+ll][i+1][ll]+d)%mod;
                    }
                }
        }
        cout<<ans<<endl;
    }
    return 0;
}

1005 gcd

首先证明一个小结论:
x1时,不妨设a>b:

(xa1)1(modx)

gcd(xa1,xab)=1

gcd(xa1,xb1)=gcd(xa1,xaxab)=gcd(xab1,xa1)

由此可以得出:
gcd(xa1,xb1)=xgcd(a,b)1

gcd(xa1,xb1)=xgcd(a,b)1

枚举d=gcd(a,b),设其出现次数为s[d],易得
s[d]=2(ϕ(1)+ϕ(2)++ϕ(nd))1

我们发现s[d]的值只与nd的有关,且这个值在1n上有n段,我们可以枚举并二分出区间,对于每个区间l,r,对答案的贡献为
s[nl]i=lr(xi1)

s数组的值可以预处理出来,后面那部分可以利用等比数列公式和乘法逆元求得,总的时间复杂度为O(n+Tnlogn)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;

int T;
const int maxn=1100000;
const LL mod=1e9+7;
LL phi[maxn];
LL sum[maxn];
LL ans;
LL x,n;
LL l,r;
void getPhi()
{
    for (int i=1;i<maxn;i++)
        phi[i]=i;
    for (LL i=2;i<maxn;i++)
        if (i==phi[i])
            for (LL j=i;j<maxn;j+=i)
                phi[j]=(phi[j]/i)*(i-1);
}
LL cal(LL x)
{
    LL l=1,r=n,ans;
    while (l<=r)
    {
        LL mid=l+r>>1;
        if (n/mid>=x) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
LL extended_gcd(LL a,LL b,LL &x,LL &y)
{
    if (b==0)
    {
        x=1,y=0;
        return a;
    }
    else
    {
        LL r=extended_gcd(b,a%b,y,x);
        y-=x*(a/b);
        return r;
    }
}
LL pow_mod(LL a,LL b,LL p)
{
    LL tmp=1;
    a%=p;
    for(LL i=b;i;i>>=1,a=a*a%p)
        if(i&1)tmp=tmp*a%p;
    return tmp;
}
LL inv(LL a,LL n)
{
    LL xx,yy,d=extended_gcd(a,n,xx,yy);
    return (xx%n+n)%n;
}
LL cc(LL l,LL r)
{
    LL res=(pow_mod(x,r+1,mod)-pow_mod(x,l,mod)+mod)*(inv(x-1,mod))%mod;
    res=(res-r+l-1+mod)%mod;
    return res;
}
int main()
{
    getPhi();
    for (int i=1;i<maxn;i++)
        sum[i]=(sum[i-1]+phi[i])%mod;
    for (int i=1;i<maxn;i++)
        sum[i]=(2*sum[i]-1)%mod;
    cin>>T;
    while (T--)
    {
        cin>>x>>n;
        if (x==1)
        {
            cout<<0<<endl;
            continue;
        }
        ans=0;
        l=1;
        while (l<=n)
        {
            r=cal(n/l);
            ans=(ans+cc(l,r)*sum[n/l])%mod;
            l=r+1;
        }
        cout<<ans<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值