• [Atcoder Grand Contest 003] Tutorial


    Link:

    AGC003 传送门

    A:

    判断如果一个方向有,其相反方向有没有即可

    #include <bits/stdc++.h>
    
    using namespace std;
    char s[1005];
    map<char,bool> mp;
    int main()
    {
        scanf("%s",s);
        for(int i=0;i<strlen(s);i++) mp[s[i]]=true;
        if(mp['S']==mp['N']&&mp['E']==mp['W']) puts("Yes");
        else puts("No");
        return 0;
    }
    Problem A

    B:

    贪心最大化利用即可

    #include <bits/stdc++.h>
    
    using namespace std;
    int n,dat[100005];
    long long res=0;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
        for(int i=1;i<=n-1;i++)
        {
            res+=dat[i]/2;dat[i]%=2;
            if(dat[i+1]>dat[i]) dat[i+1]-=dat[i],res+=dat[i];
        }
        res+=dat[n]/2;
        printf("%lld",res);
        return 0;
    }
    Problem B

    C:

    可以将两个操作转换为:

    1、交换两个相邻的数

    2、交换两个中间相间一个的数

    由于只要求操作1的最小数,可以发现调整好所有数的序号的奇偶性就能用操作2完成任务

    因此求出原序号为奇数,排序后序号为偶数的数的个数即可(原偶现奇的个数与其相同)

    #include <bits/stdc++.h>
    
    using namespace std;
    int n,dat[100005],id[100005];
    
    bool cmp(int a,int b)
    {return dat[a]<dat[b];}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&dat[i]),id[i]=i;
        sort(id+1,id+n+1,cmp);
        int res=0;
        for(int i=1;i<=n;i++)
            if((id[i]&1)&&!(i&1)) res++;
        printf("%d",res);
        return 0;
    }
    Problem C

    D:

    思路类似于 [Codeforces#480 D] ,只不过将平方改成了立方

    要判断两个数的积为立方,可以将每个数的各个质因数次幂$mod 3$化简后再判断

    于是我们求出每个数化简后的结果,统计出现次数,同时求出化简后的数的补数

    最后在一个数及其补数中选择个数更多的那一组即可(特判:如果化简后为1只能算一个)

    实现的难点在质因数分解……

    由于$nle 10^{10}$,肯定不能求出$n$以内的所有质数

    于是要先将$10^{frac{10}{3}}$内的质因数分解掉,保证剩下的质因数的次幂不会超过2次

    实际上,剩下的数只有3种情况,分类讨论就好了:

    1、$p^2$ 2、$p$ 3、$p*q$  ($p,q$为质数)

    这样就能在$O(1)$的时间内计算大于$10^{frac{10}{3}}$的质因数的化简结果及其补数了

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MAXN=1e5+10;
    int n,pri[3005],vis[3005],tot,res=0;
    ll x[MAXN],y[MAXN],t;
    map<ll,int> mp,avl;
    
    ll cube(int a){return 1ll*a*a*a;}
    ll sqr(int a){return 1ll*a*a;}
    int main()
    {
        for(int i=2;i<=3000;i++)
        {
            if(!vis[i]) pri[++tot]=i;
            for(int j=1;j<=tot&&i*pri[j]<=3000;j++)
            {
                vis[i*pri[j]]=1;
                if(i%pri[j]==0) break;
            }
        }
        
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&t);x[i]=y[i]=1;
            for(int j=1;cube(pri[j])<=t;j++)
            {
                int cnt=0;
                while(t%pri[j]==0) t/=pri[j],cnt++;
                if(cnt%3==1) x[i]*=pri[j],y[i]*=sqr(pri[j]);
                else if(cnt%3==2) x[i]*=sqr(pri[j]),y[i]*=pri[j];
            }
            x[i]*=t;mp[x[i]]++;
            y[i]*=sqr(sqrt(t))==t?sqrt(t):sqr(t);
        }
        
        for(int i=1;i<=n;i++)
            if(!avl[x[i]])
            {
                avl[x[i]]=avl[y[i]]=1;
                if(x[i]==1) res++;
                else res+=max(mp[x[i]],mp[y[i]]);
            }
        printf("%d",res);
        return 0;
    }
    Problem D

    如果操作为除法/取模,且要枚举$n$个除数/模数时,

    学会较小数暴力,较大数分类讨论的方式(如 [ARC060 D]

    E:

    首先可以将$dat$序列改为单调递增的,其他项明显是多余的

    通过观察发现每一项可以由之前的项重复、组合而成(可能留有小于$len[1]$的后缀)

    于是我们令$cnt[i]$为第$i$项被重复的总次数,对于从后往前每一个$i$:

    每次二分找到最大的小于$dat[i]$的$dat[j]$,那么第$j$项可以多重复$dat[i]/dat[j]*cnt[i]$次

    接下来将$dat[i]$mod$dat[j]$,寻找下一个重复项

    由于一个数$mod$比自己小的数明显最多进行$log(n)$次,因此复杂度为$O(n*log(n))$

    由于每项在整块整块分解后仍可能有小于$len[1]$的后缀,需要另外统计

    为了保证时间复杂度,且由于该后缀一定连续,因此差分统计就好了

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MAXN=1e5+10;
    int n,q,tot;
    ll res[MAXN],dat[MAXN],cnt[MAXN];
    
    int Query(ll x,int r)
    {
        int ret=-1,l=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(dat[mid]<=x) ret=mid,l=mid+1;
            else r=mid-1;
        }
        return ret;
    }
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=q;i++) scanf("%lld",&dat[i]);
        dat[0]=n;//如果最小值大于n要添加n 
        for(int i=1;i<=q;i++)
        {
            while(tot>=0&&dat[i]<dat[tot]) tot--;
            dat[++tot]=dat[i];
        }
        
        cnt[tot]=1;
        for(int i=tot;i>=0;i--)
        {
            ll k=dat[i],pos=Query(k,i-1);
            while(pos>=0)
            {
                cnt[pos]+=(k/dat[pos])*cnt[i];
                k=k%dat[pos];pos=Query(k,pos-1);
            }
            res[1]+=cnt[i];res[k+1]-=cnt[i];
        }
        for(int i=1;i<=n;i++)
            res[i]+=res[i-1],printf("%lld
    ",res[i]);
        return 0;
    }
    Problem E

    F:

    %%%LCA的题解:https://loj.ac/article/189

    一眼看上去完全不可做的题(好像对我而言所有题都这样

    但先进行大的分类后其实会发现明朗很多

    令矩阵中有色点数为$cnt$,上下/左右相邻的有色点对数分别为$pv,ph$

    令原矩阵上下/左右拼接后能连通的列数/行数分别为$sv,sh$

    1、如果$sv,sh$都大于零,明显全部连通

    2、如果$sv,sh$都为零,明显答案为$cnt^{k-1}$

    3、如果$sv,sh$中恰有一个大于零,需要递推:

    将最终矩阵中每一个初始矩阵视为一个点,两点相连通则视为有一条边

    由于最终状态不可能有环,求连通块的个数,其实就是求点数与边数的差

    根据上面求出的量,可以写出如下的递推式:

    $V_k=V_{k-1}*cnt$

    $E_k=E_{k-1}*cnt+pv(h)*sv(h)$

    对于这样的式子矩乘优化就好了

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MOD=1e9+7,MAXN=1005;
    struct matrix
    {
        ll p[5][5];
        matrix(){memset(p,0,sizeof(p));}
        void init(){p[0][0]=p[1][1]=1;}
        friend matrix operator * (const matrix &a,const matrix &b)
        {
            matrix ret;
            for(int i=0;i<2;i++)
                for(int j=0;j<2;j++)
                    for(int k=0;k<2;k++)
                        (ret.p[i][j]+=a.p[i][k]*b.p[k][j]%MOD)%=MOD;
            return ret;
        }
        friend matrix quick_pow(matrix a,ll b)
        {
            matrix ret;ret.init();
            for(;b;b>>=1,a=a*a)
                if(b&1) ret=ret*a;
            return ret;
        }
    };
    
    char s[MAXN][MAXN];
    ll k;
    int n,m,cnt,pv,ph,sv,sh;
    ll quick_pow(ll a,ll b)
    {
        ll ret=1;
        for(;b;b>>=1,a=a*a%MOD)
            if(b&1) ret=ret*a%MOD;
        return ret;
    }
    
    int main()
    {
        scanf("%d%d%lld",&n,&m,&k);
        if(k<=1) return puts("1"),0;
        for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
            if(s[i][j]=='#')
            {
                cnt++;
                pv+=(s[i][j]==s[i+1][j]);
                ph+=(s[i][j]==s[i][j+1]);
            }
        for(int i=1;i<=m;i++) sv+=(s[1][i]=='#'&&s[n][i]=='#');
        for(int i=1;i<=n;i++) sh+=(s[i][1]=='#'&&s[i][m]=='#');
        
        if(sv&&sh) return puts("1"),0;
        else if(!sv&&!sh) return printf("%lld",quick_pow(cnt,k-1)),0;
        else
        {
            matrix t;t.p[0][0]=cnt;t.p[1][0]=(sv?pv:0)+(sh?ph:0);t.p[1][1]=sv+sh;
            t=quick_pow(t,k-1);printf("%lld",(t.p[0][0]-t.p[1][0]+MOD)%MOD);
        }
        return 0;
    }
    Problem F
  • 相关阅读:
    Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】
    Servlet第五篇【介绍会话技术、Cookie的API、详解、应用】
    Servlet第四篇【request对象常用方法、应用】
    Servlet第三篇【request和response简介、response的常见应用】
    Tomcat就是这么简单
    JProfiler远程监控Linux上Tomcat的安装过程细讲(步骤非常详细!!!)
    FileChannel类的理解和使用
    Lifetime-Based Memory Management for Distributed Data Processing Systems
    Spark的核心RDD(Resilient Distributed Datasets弹性分布式数据集)
    Hadoop与Spark之间的比较
  • 原文地址:https://www.cnblogs.com/newera/p/9285356.html
Copyright © 2020-2023  润新知