zoukankan      html  css  js  c++  java
  • Codeforces Round #549 (Div. 2) Solution

    传送门

    A.The Doors

    看懂题目就会写的题

    给一个 $01$ 序列,找到最早的位置使得 $0$ 或 $1$ 已经全部出现

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e6+7;
    int n,a[N];
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        int t=n-1; for(;t;t--) if(a[t]!=a[t+1]) break;
        printf("%d",t);
        return 0;
    }
    A

     B.Nirvana

    定义一个数的价值为它每一位的乘积,如 $132$ 的价值为 $1*3*2=6$ ,求所有小于等于 $n$ 的数中价值最大的数的价值

    从后往前考虑每一位,贪心地想,这一位可以要取的值要么是此位的值,要么是 $9$,如果取此位的值就不会对后面有影响

    如果取 $9$ 就相当于要往更高一位借 $1$

    设 $f[i][0/1]$ 表示从后往前考虑到第 $i$ 位,此位向不向更高一位借 $1$

    那么转移很好想,设此位的值为 $t$, $f[i][0]=max(f[i-1][0]*t,f[i-1][1]*(t-1))$

    $f[i][1]=max(f[i-1][0]*9,f[i-1][1]*9)$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline ll read()
    {
        ll x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    ll n,f[27][2];
    int main()
    {
        n=read();
        ll t=1,cnt=1;
        while(t<=n) t*=10,cnt++;
        cnt--; t=n; f[0][0]=1;
        for(ll i=1;i<=cnt;i++)
        {
            if(t%10) f[i][0]=max(f[i-1][0]*(t%10),f[i-1][1]*(t%10-1));
            f[i][1]=max(f[i-1][0]*9,f[i-1][1]*9);
            t/=10;
        }
        cout<<max(f[cnt][0],f[cnt-1][1]);//注意大一位可能被借没,所以要考虑小一位
        return 0;
    }
    B

    C.Queen

    给一颗树,有两种节点,一种不 '尊重' 所有祖先节点,一种 '尊重' 所有祖先节点

    定义一次删除操作,删除一个不尊重祖先且没有 儿子 (不是后代)尊重的节点,如果有多个,取编号最小的节点删除

    一个节点删除后,它的儿子全部接在此节点的父节点上,输出删除的顺序,如果没有节点被删除输出$-1$

    找找性质,容易证明,如果一个节点刚开始不能删除,那么之后也不能删除(一直会有儿子尊重,或自己尊重祖先)

    如果一个节点刚开始可以删除,那么之后一定会删除(儿子一定一直不尊重它,就算一个儿子删掉了,儿子的儿子也肯定不尊重祖先)

    所以删除的节点一开始就可以确定,直接 $dfs$ 求出所有编号,然后排序输出

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    int n,rt;
    bool p[N];
    vector <int> V[N];
    int tmp[N],tot;
    bool dfs(int x)
    {
        bool flag=p[x];
        for(int i=V[x].size()-1;i>=0;i--)
            flag&=dfs(V[x][i]);
        if(flag) tmp[++tot]=x;
        return p[x];
    }
    int main()
    {
        int a; n=read();
        for(int i=1;i<=n;i++)
        {
            a=read(),p[i]=read();
            if(a!=-1) V[a].push_back(i);
            else rt=i;
        }
        dfs(rt);
        sort(tmp+1,tmp+tot+1);
        if(!tot) { printf("-1"); return 0; }
        for(int i=1;i<=tot;i++) printf("%d ",tmp[i]);
        return 0;
    }
    C

    D.The Beatles

    我原来题意一直没看懂啊...难怪不会写...

    首先可以知道环的周长为 $S=n*k$

    发现环是对称的,所以起点在哪个餐馆旁对答案没有影响,只要枚举终点在哪个餐馆旁,设 起点到终点的距离(同时也是步长)为 $L$,走的步数为 $stp$,那么就是要找到最小的 $t$ 使得 $S*t/L==stp$ 整除,此时 $S*t/L$ 就是步数,所以 $stp=S/gcd(L,S)$ ,然后就完了

    注意起点和终点都分为在餐馆左边和右边两种情况

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    int n,K,A,B;
    ll gcd (ll a, ll b) { return b ? gcd(b, a % b) : a; }
    ll Mi,Mx;
    void solve(ll p)
    {
        ll s=1ll*n*K,pos,t,L,stp;
        for(int i=1;i<=n;i++)
        {
            pos=1ll*(i-1)*K+1;
            t=pos-B; if(t<0) t+=s;
            L=t-p; if(L<0) L+=s;
            stp=s/gcd(s,L);
            Mi=min(Mi,stp); Mx=max(Mx,stp);
            t=pos+B; if(t>s) t-=s;
            L=t-p; if(L<0) L+=s;
            stp=s/gcd(s,L);
            Mi=min(Mi,stp); Mx=max(Mx,stp);
        }
    }
    int main()
    {
        n=read(),K=read(),A=read(),B=read();
        Mi=1ll*n*K;
        solve(A+1);
        solve(K+1-A);
        cout<<Mi<<" "<<Mx<<endl;
        return 0;
    }
    D

    E.Lynyrd Skynyrd

    给定一个排列和一个数列,多次询问,求数列区间 $l,r$ 中是否存在一个子序列(不是子串),使得此子序列经过循环移位后为给定的那个排列

    定义一个序列的循环移位为 对于序列 $p[1],p[2]...p[n]$ 它的一个循环移位为 $p[k],p[k+1]...p[n]p[1]p[2]...p[k-1]$

    考虑维护数列每个数在排列中的上一个数 上一次在数列中出现的位置 $pre$ ,贪心地想,要匹配一定是优先到最近的合法位置($pre$位置)

    容易考虑对区间的每个数往前暴力走,如果走完 $n-1$ 步还在区间 $l,r$ 中则存在

    如果每个数暴力走完发现都无解了,则不存在

    考虑优化这个方法,发现我们暴力每次只跳一步,考虑直接预处理出暴力跳 $n-1$ 步时的位置,然后用$st$表维护一个区间的数跳 $n-1$ 步后的最大位置

    如果最大位置大于等于 $l$ 则有解

    发现还是不够,预处理每次只跳一步太慢了,考虑先维护倍增数组 $F[i][j]$ 表示位置 $i$ 跳 $2^j$ 步后到达的位置,这样预处理时只要跳 $log_n$ 次

    然后就可以过了,复杂度 $O(nlog_n)$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    int n,m,Q;
    int A[N],B[N],pos[N],las[N],Log[N];
    //A是排列,B是数列,pos[i]表示值i在排列中的位置,las动态维护一个数在数列中上一次出现的位置
    int F[N][21],G[N][21];//F[i][j]是位置i跳2^j步后到达的位置,G[i][j]是ST表用来维护一个区间跳n-1步后到达的最右位置
    int main()
    {
        n=read(),m=read(),Q=read();
        for(int i=1;i<=n;i++) A[i]=read(),pos[A[i]]=i;
        for(int i=1;i<=m;i++) B[i]=read();
        for(int i=1;i<=m;i++)
        {
            int t=A[ pos[B[i]]-1 ];
            if(!t) t=A[n];//考虑循环置换
            F[i][0]=las[t]; las[B[i]]=i;
        }
        for(int i=1;(1<<i)<=n;i++)
            for(int j=1;j<=m;j++) F[j][i]=F[F[j][i-1]][i-1];//处理F
        for(int i=1;i<=m;i++)
        {
            int t=n-1,p=i;
            for(int j=20;j>=0;j--)
                if(t-(1<<j)>=0) t-=(1<<j),p=F[p][j];//跳n-1步
            G[i][0]=p;//处理G[i][0]
        }
        Log[0]=-1; for(int i=1;i<=m;i++) Log[i]=Log[i>>1]+1;
        for(int i=1;(1<<i)<=m;i++)
            for(int j=1;j+(1<<i)-1<=m;j++) G[j][i]=max(G[j][i-1],G[j+(1<<i-1)][i-1]);//维护G
        while(Q--)
        {
            int l=read(),r=read();
            int t=Log[r-l+1];
            if(max(G[l][t],G[r-(1<<t)+1][t])>=l) printf("1");//处理询问
            else printf("0");
        }
        return 0;
    }
    E

    FU2

    给一堆点,和一个抛物线 $y=x^2+bx+c$ ,显然把此抛物线代入任意两个横坐标不同的点可以确定它的形状

    求有多少对点确定的抛物线中,没有任何点在它上方,如果有多组点确定的是同一条抛物线那么只算一次

    发现这个二次项很恶心,如果给的是直线直接求上凸包就行了,考虑消掉二次项

    把坐标进行变换 :$(x,y)-->(x,y-x^2)$,然后发现坐标系变换后原来的图像变成了直线

    然后同样求上凸包就好了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    typedef double db;
    inline ll read()
    {
        ll x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    struct Poi{
        ll x,y;
        inline bool operator < (const Poi &tmp) const {
            return x!=tmp.x ? x<tmp.x : y<tmp.y;
        }
    }p[N];
    inline bool fc(Poi A,Poi B,Poi C) { return (B.y-A.y)*(C.x-A.x) >= (C.y-A.y)*(B.x-A.x); }
    // 如果 (B.y-A.y)/(B.x-A.x)>=(C.y-A.y)/(C.x-A.x) 成立则返回1
    int n,ans;
    int st[N],Top;
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
        {
            p[i].x=read(); p[i].y=read();
            p[i].y-=p[i].x*p[i].x;
        }
        sort(p+1,p+n+1);
        st[Top=1]=n;
        for(int i=n-1;i;i--)
        {
            while(Top>1 && fc(p[st[Top-1]],p[st[Top]],p[i]) ) Top--;
            st[++Top]=i;
        }
        for(int i=1;i<=Top;i++) if(i==1||p[st[i]].x!=p[st[i-1]].x) ans++;
        printf("%d",ans-1);
        return 0;
    }
    F
  • 相关阅读:
    《C++标准程序库》 第6章 STL Container
    《C++语言99个常见编程错误》
    单例模式
    《C++标准程序库》 第7章 Iterator Adapters
    Shell颜色封装(C++)
    《改善C++程序的150个建议》
    OpenCV之图片的创建、保存和复制
    XMLDOM对象方法:对象事件
    三国中最精辟的十句话
    中国十大名茶及鉴别方法
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10633767.html
Copyright © 2011-2022 走看看