牛客练习赛27游记

牛客练习赛27游记

A - 纸牌

题目大意:

有两张纸牌,两张纸牌上有相同的正整数\(n(n\le10^9)\)
每一轮一张纸牌上的数都可以减去小于等于另外一张纸牌上的数的非负数。
每一轮只能操作和上轮不同的纸牌。
求三轮之后两张纸牌上数字之和的最小值。

思路:

答案就是\(\lceil\frac n2\rceil\)

源代码:

#include<cstdio>
#include<cctype>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
int main() {
    const int n=getint();
    printf("%d\n",n/2+n%2);
    return 0;
}

B - 手办

题目大意:

给定\(n(n\le10^{12})\),求\(\sum_{k=1}^n\sum_{a=1}^k\sum_{b=1}^k[ab|k]\)

思路:

题目就是要求满足\(abc\le n\)的三元组\((a,b,c)\)个数。

\(a\le b\le c\),那么\(a\)枚举到\(\sqrt[3]n\)\(b\)在剩下\(\sqrt{\frac na}\)中枚举,\(c\)的个数可以直接算出来。

交换\(a,b,c\)总共会有\(6\)种方案。

另外注意判断\(2\)个数和\(3\)个数相同的情况。

源代码:

#include<cstdio>
#include<cctype>
typedef long long int64;
inline int64 getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int64 x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int mod=2333;
int main() {
    const int64 n=getint();
    int ans=0;
    for(register int i=1;(int64)i*i*i<=n;i++) {
        (ans+=1+(n/i/i-i)*3%mod)%=mod;
        for(register int j=i+1;(int64)j*j<=n/i;j++) {
            (ans+=3+(n/i/j-j)*6%mod)%=mod;
        }
    }
    printf("%d\n",ans);
    return 0;
}

C - 水图

题目大意:

一棵\(n(n\le50000)\)个结点的带边权的树,求从\(x\)出发经过每个点至少一次最少需要走多少路。

思路:

所有边权和×2-从x出发能走最远的距离。

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=50001;
typedef long long int64;
int64 dep[N],max;
struct Edge {
    int to,w;
};
std::vector<Edge> e[N];
inline void add_edge(const int &u,const int &v,const int &w) {
    e[u].push_back((Edge){v,w});
    e[v].push_back((Edge){u,w});
}
void dfs(const int &x,const int &par) {
    for(auto &j:e[x]) {
        const int &y=j.to,&w=j.w;
        if(y==par) continue;
        dep[y]=dep[x]+w;
        dfs(y,x);
    }
    max=std::max(max,dep[x]);
}
int main() {
    const int n=getint(),x=getint();
    int64 ans=0;
    for(register int i=1;i<n;i++) {
        const int u=getint(),v=getint(),w=getint();
        add_edge(u,v,w);
        ans+=(int64)w*2;
    }
    dfs(x,0);
    ans-=max;
    printf("%lld\n",ans);
    return 0;
}

D - 愤怒

题目大意:

将一个长度为\(n(n\le10000)\)的序列划分为恰好两个子序列,使得两个子序列合法,求方案数。

思路:

左括号当成\(1\),右括号当\(-1\)\(f_{i,j}\)表示考虑完前\(i\)个字符,第一个序列的前缀和是\(j\)的方案数。考虑下一个字符时,可以将其加入第一个序列和第二个序列。如果前缀和\(<0\)就不合法,否则就可以转移。

时间复杂度\(\mathcal O(n^2)\)

源代码:

#include<cstdio>
#include<cctype>
#include<cstring>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
inline bool check(const char &ch) {
    return ch=='('||ch==')';
}
inline int getval() {
    register char ch;
    while(!check(ch=getchar()));
    if(ch=='(') return 1;
    if(ch==')') return -1;
    return 0;
}
const int N=10001,mod=2333;
int f[2][N],sum[N];
int main() {
    const int n=getint();
    f[0][0]=1;
    for(register int i=1;i<=n;i++) {
        const int cur=i&1;
        const int x=getval();
        sum[i]=sum[i-1]+x;
        if(sum[i]<0) {
            puts("0");
            return 0;
        }
        memset(f[cur],0,sizeof f[cur]);
        for(register int j=0;j<=n;j++) {
            if(0<=j+x&&j+x<=n) {
                (f[cur][j+x]+=f[!cur][j])%=mod;
            }
            if(sum[i]-j>=0) {
                (f[cur][j]+=f[!cur][j])%=mod;
            }
        }
    }
    printf("%d\n",f[n&1][0]);
    return 0;
}

E - 欧拉

题目大意:

\(m(m\le5\times10^5)\)次询问,每次给出\(n(n\le5\times10^6)\),求\(F(n)=\sum_{d|n}d^k\mu(\frac nd)\)。模\(998244353\)

思路:

\(F(n)\)是一个积性函数,直接线性筛即可。

源代码:

#include<cstdio>
#include<cctype>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
typedef long long int64;
const int N=5e6+1,mod=998244353;
int m,k;
inline int power(int a,int k) {
    int ret=1;
    for(;k;k>>=1) {
        if(k&1) ret=(int64)ret*a%mod;
        a=(int64)a*a%mod;
    }
    return ret;
}
int p[N],f[N];
bool vis[N];
inline void sieve() {
    for(register int i=2;i<N;i++) {
        if(!vis[i]) {
            p[++p[0]]=i;
            f[i]=power(i,k)-1;
        }
        for(register int j=1;j<=p[0]&&i*p[j]<N;j++) {
            vis[i*p[j]]=true;
            f[i*p[j]]=(int64)f[i]*(f[p[j]]+!(i%p[j]))%mod;
            if(i%p[j]==0) break;
        }
    }
}
int main() {
    m=getint(),k=getint();
    sieve();
    for(register int i=0;i<m;i++) {
        printf("%d\n",f[getint()]);
    }
    return 0;
}

F - 计数

转载于:https://www.cnblogs.com/skylee03/p/9689347.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值