返回顶部

AtCoder Beginner Contest 220 A~F 题解

AtCoder Beginner Contest 220

A - Find Multiple

水题

B - Base K

水题

C - Long Sequence

  • 题意:有长度为\(n\)的序列\(A\)\(B\)由连续\(10^{100}\)\(A\)拼接而成,给你一个\(X\),问你\(B\)的最小的前缀和位置\(k\),使得\(\sum^{k}_{i=1}B_i>X\).

  • 题解:先统计\(A\)\(sum\)和前缀和,\(cnt\)\(X|sum\)的块数,\(rest\)为剩下的余数,然后二分找\(rest\)\(A\)的前缀和第一个大于的位置\(pos\)\(cnt*n+rest\)即为答案。

  • 代码

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n;
    ll a[N];
    ll x;
    ll pre[N];
    
    int main() {
        scanf("%d",&n);
        ll sum=0;
        for(int i=1;i<=n;++i){
            scanf("%lld",&a[i]);
            sum+=a[i];
            pre[i]=pre[i-1]+a[i];
        }
        scanf("%lld",&x);
        ll cnt=x/sum;
        ll rest=x%sum;
        int pos=upper_bound(pre+1,pre+1+n,rest)-pre;
        printf("%lld\n",cnt*n+pos);
        return 0;
    }
    
    

D - FG operation

  • 题意:有长度为\(n\)的序列\(A\),每个元素由\([0,9]\)构成,每次有两种操作,一是取出序列的前两个元素\(x\)\(y\),pop掉,然后在队头放入\((x+y)\mod 10\).另外一种是放入\((x*y)\mod 10\),问你最后序列剩一个数的时候\([0,9]\)的方案数。

  • 题解:元素范围\([0,9]\),很明显可以二维dp,设\(dp[i][now]\)表示第\(i\)次操作,操作完后队头元素为\(now\)的方案数,那么我们枚举上一个位置的所有可能情况\([0,9]\),得到:\(dp[i][now]=dp[i][now]+dp[i-1][j]\).结束!

  • 代码

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e5 + 10;
    const int mod = 998244353;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n;
    ll a[N];
    ll dp[N][10];
    
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%lld",&a[i]);
        }
        dp[1][a[1]]=1;
        for(int i=2;i<=n;++i){
            for(int j=0;j<10;++j){
                dp[i][(a[i]+j)%10]=(dp[i][(a[i]+j)%10]+dp[i-1][j])%mod;
                dp[i][(a[i]*j)%10]=(dp[i][(a[i]*j)%10]+dp[i-1][j])%mod;
            }
        }
        for(int i=0;i<10;++i){
            printf("%lld\n",dp[n][i]);
        }
        return 0;
    }
    
    

E - Distance on Large Perfect Binary Tree

  • 题意:有一颗\(2^n-1\)个结点的完全二叉树,边权为\(1\),问你有多少点对\((i,j)\)它们的最短路径为\(D\).注意\((i,j)\)\((j,i)\)不相同。

  • 题解:对每个点考虑贡献,那么有两种情况,一种是只在左子树或者右子树上有点对,这个时候路径是一条链,那么满足条件的点就有\(2^{D-1}\)个,因为左子树和右子树等同,所以贡献为\(2^D\)。再来考虑\(D\)被左右子树分摊的情况,这时候左子树和右子树都至少要贡献一个距离,我们假设左或右子树能贡献的最远距离为\(r\ (r\ge 1)\),那么另外一颗子树能贡献的最小距离\(l=d-r\ (l\ge 1)\),想象一下一根链子挂在父亲结点左右滑动,那么就有\(r-l+1\)种情况,假设左子树的距离为\(l\),右子树的距离为\(r\),那么贡献为\((r-l+1)*2^{l-1}*2^{r-1}=(r-l+1)*2^{D-2}\)。因为是一颗完全二叉树,所以只用每一层的一个结点,然后乘上那一层结点的个数即可\(O(1)\)算每一层的结点的贡献,还要注意判断距离是否合法,即距离是否超过了当前结点到叶子结点的距离。最后对答案*2即可。

  • 代码

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 3e6 + 10;
    const int mod = 998244353;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n,d;
    ll pw[N];
    
    int main() {
        scanf("%d %d",&n,&d);
        pw[0]=1;    
        for(int i=1;i<=2*n;++i){
            pw[i]=(pw[i-1]*1ll*2)%mod;
        }
        ll ans=0;
        for(int i=1;i<=n;++i){
            int mx=n-i;
            if(d<=mx){  
                ans=(ans+pw[d]*pw[i-1]%mod)%mod;
            }
            int r=min(mx,d-1);  //左右子树分摊(即左右子树至少存在一个点),r表示左或右子树能到达的最远的度
            int l=d-r;
            if(l>r) continue;
            ans=(ans+(r-l+1)*pw[d-2]%mod*pw[i-1]%mod)%mod;
        }
        printf("%lld\n",ans*2%mod);
        return 0;
    }
    
    

F - Distance Sums 2

  • 题意:一颗\(n\)个结点的树,边权为\(1\),对于每个点\(i\),计算\(\sum^n_{j=1}dis(i,j)\)\(dis(i,j)\)表示\(i\)\(j\)的最短距离。

  • 题解:这\(F\)题比上一题简单的多,也就是计算每个点到根节点的距离之和,假设现在根节点为\(i\)\(j\)\(i\)的一个儿子,我们现在知道\(i\)的总贡献\(ans[i]\),那么\(j\)的总贡献其实是可以\(O(1)\)计算的,具体看图理解:

    注意看红色圈起来的那条边,只有这条边造成了贡献的变化,当\(j\)变成根节点后,这条边原本是包含\(j\)在内的其所有儿子的必经之路,即贡献为\(sz[j]\),但现在根节点变成\(j\)了,这条边的贡献是多余的,给它减去,但是现在这条边是我们绿的这一块部分的必经之路,所以多出的贡献为\(n-sz[j]\),那么\(j\)的总贡献\(ans[j]=ans[i]-sz[j]+n-sz[j]\).

    所以我们可以先dfs找到\(1\)的总贡献,然后再跑一遍dfs计算每个点的答案。

  • 代码

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n;
    vector<int> edge[N];
    int sz[N];
    ll ans[N];
    
    void dfs(int u,int fa,int dep){
       for(auto to:edge[u]){
            if(to==fa) continue;
            ans[1]+=dep;
            dfs(to,u,dep+1);
            sz[u]+=sz[to]; 
       } 
       sz[u]++;
    }
    
    void cal(int u,int fa){
        if(u!=1) ans[u]=ans[fa]-sz[u]+n-sz[u];
        for(auto to:edge[u]){
            if(to==fa) continue;
            cal(to,u);
        }
    }
    
    int main() {
        scanf("%d",&n);
        for(int i=1;i<n;++i){
            int u,v;
            scanf("%d %d",&u,&v);
            edge[u].pb(v),edge[v].pb(u); 
        }
        dfs(1,-1,1);
        cal(1,-1);
        for(int i=1;i<=n;++i) printf("%lld\n",ans[i]);
        return 0;
    }
    
    
    
posted @ 2021-09-27 22:14  Rayotaku  阅读(181)  评论(0编辑  收藏  举报