Codeforces Round #605 (Div. 3)

7976 ワード

A. Three Friends
タイトルリンク
テーマの大意
3つの数(a,b,c)をあげて、各数は左、右またはその場で動かないことを選択して、(minleft(left|a-bright|+left|a-cright|+left|b-cright|right)の値を求めることができます)
問題を解く構想.
  • まず定義に従って答えを求め、3つの数が待ちたい場合は0を直接出力し、2つの数が連続している場合は(2)を減算し、そうでない場合は(4)を減算する.
  • は依然として定義に従って答えを求め、4未満であれば0を出力し、そうでなければ4出力を減算する.

  • ACコード1
    #include 
    const int maxn = 1e5+100;
    typedef long long ll;
    using namespace std;
    int q;
    ll a[4];
    int main()
    {
        // freopen("data.txt","r",stdin);
        cin>>q;
        while(q--)
        {
            for(int i=0;i<3;i++)
            {
                cin>>a[i];
            }
            sort(a,a+3);
            ll res = a[1]-a[0]+a[2]-a[0]+a[2]-a[1];
            if(a[0]==a[1]&&a[1]==a[2]){
     
            }
            if(a[0]==a[1]&&a[1]+1==a[2]){
                res-=2;
            }else if(a[1]==a[2]&&a[0]+1==a[1]){
                res-=2;
            }else{
                res-=4;
            }
            res = max(1LL*0,res);
            cout<

    ACコード2
    #include 
    typedef long long ll;
    using namespace std;
    int main()
    {
        int q;
        cin>>q;
        while(q--){
            int a,b,c;
            cin>>a>>b>>c;
            int res = abs(a-b)+abs(b-c)+abs(a-c);
            res = (res>4)?res-4:0;
            cout<

    B. Snow Walking Robot
    タイトルリンク
    テーマの大意
    1つの四角い格子の中で、(0,0)点から出発して、毎回上へ、下へ、右へ、左へ1つの格子を前進することができて、それぞれ(U,D,R,L)の文字で表して、あなたに(U,D,R,L)だけを含む文字列をあげて、できるだけ少ない文字を削除してそして新しいソートをやり直して、同時に各点が最大1回通過して、最後に(0,0)のこの点に戻って、最後の文字列を出力します.
    問題を解く構想.
    それぞれの統計(U,D,R,L)の個数が、最後に(0,0)という点に戻るようにするには、(U)と(D)の文字の個数が同じでなければならず、(R)と(L)の文字の個数が同じでなければならない場合、両者の最大値を取って、それぞれ出力すればよい.
    ACコード
    #include 
    const int maxn = 1e5+100;
    typedef long long ll;
    using namespace std;
    int main()
    {
        // freopen("data.txt","r",stdin);
        int q;
        cin>>q;
        while(q--){
            string s;
            cin>>s;
            int a=0,b=0,c=0,d=0;
            for(int i=0;i

    C. Yet Another Broken Keyboard
    タイトルリンク
    テーマの大意
    文字列(s)、同時に(k)文字の文字配列(t)をあげて、(s)の中で(t)の中の文字だけを含む文字列の個数を求めます.
    問題を解く構想.
    まず文字配列(t)を巡回し、別の配列(str)で各文字が利用可能か否かをマークし、文字列全体を巡回し、要求に合致する最長文字列長(len)を求め、答えに(frac{ntimesleft(n+1right)}{2})を加えることで可能である.
    ACコード
    #include 
    const int maxn = 1e5+100;
    typedef long long ll;
    using namespace std;
    bool str[200];
    int main()
    {
        // freopen("data.txt","r",stdin);
        for(int i=0;i<200;i++){
            str[i]=true;
        }
        int n,k;
        cin>>n>>k;
        string s;
        cin>>s;
        getchar();
        for(int i=0;i>c;
            str[c-'0']=false;
        }
        ll res=0;
        int cnt=0;
        for(int i=0;i

    D. Remove One Element
    タイトルリンク
    テーマの大意
    (n)個の数字を含む配列(a)を与えて、配列(a)の中で最大1つの配列を削除することができて、最長で厳格にサブ配列の長さを増加します.
    問題を解く構想.
    この問題は典型的な動的計画の問題である.
    配列dp[i]は(a_i)を起点とする最長子配列長を表し、遷移方程式は[dp[i]=begin{cases}1&i=n\1&a[i]geq a[i+1]\dp[i+1]+1&a[i]配列dp 2[i]は(a_i)を終点とする最長子配列長を表し、遷移方程式は[dp 2[i]=begin{cases}1&i=1\1&a[i-1]geq a[i]\dp[i-1]+1&a[i-1]である 配列全体を巡回(a)、(a_{i+1}>a_{i-1})の場合、結果は(max(result,dp 2[i+1]+dp[i-1]))となる。
    ACコード
    #include 
    const int maxn = 2e5+100;
    typedef long long ll;
    using namespace std;
    int number[maxn];
    ll dp[maxn];
    ll dp2[maxn];
    int main()
    {
        // freopen("data.txt","r",stdin);
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>number[i];
        }
        dp[n]=1;
        ll res=1;
        for(int i=n-1;i>=1;i--){
            if(number[i]number[i-1]){
                dp2[i] = dp2[i-1]+1;
            }else{
                dp2[i]=1;
            }
        }
        for(int i=1;i<=n;i++){
            if(i+1<=n&&number[i+1]>number[i-1]){
                res=max(res,dp[i+1]+dp2[i-1]);
            }
        }
        cout<

    E. Nearest Opposite Parity
    テーマの大意
    (n)個の整数からなる配列をあげます.ワンタッチで移動すると、位置(i)から位置(i-a_i)((1leq(i-a_i))の場合)または位置(i+a_i)((i+a_ileq n)の場合)にジャンプできます.
    (1)から(n)までの各位置(i)について、任意の位置(j)に到達するために必要な最小移動回数を知りたい場合は、(a_j)と(a_i)が反対のパリティを持つようにします(すなわち、(a_i)が奇数の場合は、(a_j)は偶数でなければなりません.逆も同様です).
    問題を解く構想.
  • 構想一:逆方向構築図は、偶数の場合、偶数(a_i)から奇数(a_j)までの最小移動回数逆方向構築図の後に、すべての奇数(a_j)から(a_i)までの最短経路の長さが即位する答えであるが、偶数はそれほど多く、各偶数はすべての奇数からその最短経路を計算し、時間の複雑さが高すぎる.このときスーパーソースポイントを使って、1つのポイントからすべての奇数までの距離を(0)に設定して、最短のパスを走って出て、すべての偶数の答えが出てきました.奇数の場合も同じです.つまり、2つのスーパーソースポイントを見つけて最短パスを2回走ります.
  • 構想2:同様に逆方向建図であるが、最短経路ではなく、建図中にジャンプして到達できる点を見つけてキューに入れ、BFS検索すればよい.このようなコード量を大幅に減らすことができます.

  • ACコード
    #include 
    #define INF 0x3f3f3f3f3f3f3f
    const int maxn=2e5+10;
    typedef long long ll;
    using namespace std;
    typedef pair P;
    struct edge
    {
        int to,cost;
    };
    vectorG[maxn];
    int number[maxn];
    ll d[maxn];
    int n;
    ll res[maxn];
    void dijks(int s)
    {
        priority_queue

    ,greater

    >pq; fill(d,d+n+1,INF); d[s]=0; pq.push(P(0,s)); while(!pq.empty()) { P p=pq.top(); pq.pop(); int v=p.second; if(d[v]

    d[v]+G[v][i].cost) { d[G[v][i].to]=d[v]+G[v][i].cost; pq.push(P(d[G[v][i].to],G[v][i].to)); } } } } } int main() { cin>>n; for(int i=1;i<=n;i++){ cin>>number[i]; } for(int i=1;i<=n;i++){ if(number[i]+i<=n){ edge e = {i,1}; G[number[i]+i].push_back(e); } if(i-number[i]>=1){ edge e = {i,1}; G[i-number[i]].push_back(e); } } for(int i=1;i<=n;i++){ if(number[i]%2==1){ edge e = {i,0}; G[0].push_back(e); } } dijks(0); for(int i=1;i<=n;i++){ if(number[i]%2==0){ res[i] = d[i]; } } G[0].clear(); for(int i=1;i<=n;i++){ if(number[i]%2==0){ edge e = {i,0}; G[0].push_back(e); } } dijks(0); for(int i=1;i<=n;i++){ if(number[i]%2==1){ res[i] = d[i]; } } for(int i=1;i<=n;i++){ if(res[i]>=INF){ cout<

    F. Two Bracket Sequences
    テーマの大意
    カッコ文字列(s,t)を2つあげて、最も短い合法的な文字列がそのサブシーケンスに文字列(s,t)を含むことを満たすことを見つけます.
    問題を解く構想.
    この問題もダイナミックプランニングの問題ですが、この問題は3 Dダイナミックプランニングです.(dp[i][j][k])は(s)列が(i)に一致し、(t)列が(j)に一致したときに(k)個()個が一致しない最小長さを表す.移行方程式は書きにくいので、具体的にはコードを見てください.サブシーケンスに(s),(t)が含まれ、最後の文字列ができるだけ短くなるようにするには、文字列(s),(t)で合法的なカッコをできるだけ構築する必要があります.
    ACコード
    #include 
    #define INF 0x3f3f3f3f
    const int maxn = 2e2+40;
    typedef long long ll;
    using namespace std;
    int n,m;
    string s,t;
    bool res[maxn][maxn][2*maxn];
    int dp[maxn][maxn][2*maxn];
    int dfs(int i,int j,int cnt){
        if(i==n&&j==m)
            return cnt;
        if(cnt>n+m){
            return 1e9;
        }
        if(~dp[i][j][cnt]){
            return dp[i][j][cnt];
        }
        int op1 = 1 + dfs(i+(i>s>>t;
        n = s.size();
        m = t.size();
        dfs(0,0,0);
        int i=0,j=0,cnt=0;
        while(i

    まとめ
    やっと(CodeForces)の問題を完全に補完できるようになりました.(A)問題は本当に馬鹿で、そんなに簡単で、その時は意外にも長い間詰まっていました.(B)、(C,D)問題は比較的簡単で、ただ(D)小さな細部がうまく処理されていないので、(wa)は2回も処理されました.(E)、(F)は試合後の交流の後に補充されたものです.とうとう点数が上がった,ううううう