1. 程式人生 > 其它 >Codeforces Round #737 (Div. 2) 部分題題解

Codeforces Round #737 (Div. 2) 部分題題解

D.Ezzat and Grid

題目連結

Ezzat and Grid

簡要題解

對於這一題,我們不難想出一個\(O(n^2)\)\(Dp\):設\(F[i]\)表示,使前\(i\)行合法,且留下第\(i\)行的最小代價。
那麼轉移的話,就是列舉一個合法的\(j\),使得第\(i\)行和能夠和第\(j\)行相鄰,並將中間的行全部刪掉,我們有轉移方程:

\[F[i]=Min_j(F[j]+i-j-1) \]

現在考慮優化這個\(Dp\),移項我們可以得到:

\[F[i]-i+1=Min_j(F[j]-j) \]

因此我們只需要對於合法的\(j\),找到最小的\(F[j]-j\)即可。
什麼時候合法呢?當第\(i\)

行和第\(j\)行的區間有交的時候就合法。
於是不難想到區間取\(Min\)和區間查詢操作,我們在第\(i\)行的區間上查詢\(F[j]-j\)的最小值,然後用\(F[i]-i\)更新第\(i\)行的區間即可。
這個可以用線段樹實現,加上離散化可以解決值域區間過大的問題。
時間複雜度\(O(n*logn)\)
程式碼如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=3e5+10;
const int Inf=1e9;
struct SEC{   int Le,Ri;   };
int Lsh[MAXN*2];
int n,m,Ans,Ls,F[MAXN],Lf[MAXN],Have[MAXN];
vector<SEC>Sec[MAXN];
int Read()
{   int a=0,c=1;   char b=getchar();
    while(b!='-'&&(b<'0'||b>'9')) b=getchar();
    if(b=='-') c=-1,b=getchar();
    while(b>='0'&&b<='9') a=a*10+b-48,b=getchar();
    return a*c;
}
namespace TREE
{   struct ELE{   int N,P;   }Mins[MAXN*8],Lan[MAXN*8],Nv;
    bool operator < (ELE A,ELE B){   return A.N<B.N;   }
    ELE Min(ELE A,ELE B){   return A.N<B.N?A:B;   }
    void Push_up(int S){   Mins[S]=Min(Mins[S<<1],Mins[S<<1|1]);   }
    void Push_down(int S)
    {   if(Lan[S].N>=0) return ;
        Mins[S<<1]=Min(Mins[S<<1],Lan[S]),Lan[S<<1]=Min(Lan[S<<1],Lan[S]);
        Mins[S<<1|1]=Min(Mins[S<<1|1],Lan[S]),Lan[S<<1|1]=Min(Lan[S<<1|1],Lan[S]);
        Lan[S].N=0;
    }
    ELE Query(int S,int Le,int Ri,int Al,int Ar,int Mid=0)
    {   if(Al<=Le&&Ri<=Ar) return Mins[S];
        Mid=(Le+Ri)>>1,Push_down(S);
        return Min(Al<=Mid?Query(S<<1,Le,Mid,Al,Ar):(ELE){Inf,0},Mid<Ar?Query(S<<1|1,Mid+1,Ri,Al,Ar):(ELE){Inf,0});
    }
    int Modify(int S,int Le,int Ri,int Al,int Ar,ELE Tv,int Mid=0)
    {   if(Al<=Le&&Ri<=Ar) return Mins[S]=Min(Mins[S],Tv),Lan[S]=Min(Lan[S],Tv),0;
        Mid=(Le+Ri)>>1,Push_down(S);
        if(Al<=Mid) Modify(S<<1,Le,Mid,Al,Ar,Tv);
        if(Mid<Ar) Modify(S<<1|1,Mid+1,Ri,Al,Ar,Tv);
        return Push_up(S),0;
    }
}using namespace TREE;
int main()
{   n=Read(),m=Read();
    for(int i=1,H,L,R;i<=m;i++) H=Read(),L=Read(),R=Read(),Sec[H].push_back((SEC){L,R}),Lsh[++Ls]=L,Lsh[++Ls]=R;
    sort(Lsh+1,Lsh+Ls+1),Ls=unique(Lsh+1,Lsh+Ls+1)-Lsh-1;
    for(int i=1;i<=n;i++)
        for(SEC &Ns:Sec[i]) Ns.Le=lower_bound(Lsh+1,Lsh+Ls+1,Ns.Le)-Lsh,Ns.Ri=lower_bound(Lsh+1,Lsh+Ls+1,Ns.Ri)-Lsh;
    for(int i=1;i<=n;i++)
    {   F[i]=i-1;
        for(SEC Ns:Sec[i])
            if(F[i]>(Nv=Query(1,1,Ls,Ns.Le,Ns.Ri)).N+i-1) F[i]=Nv.N+i-1,Lf[i]=Nv.P;
        for(SEC Ns:Sec[i]) Modify(1,1,Ls,Ns.Le,Ns.Ri,(ELE){F[i]-i,i});
    }
    for(int i=1;i<=n;i++)
        if(F[i]+n-i<F[Ans]+n-Ans) Ans=i;
    for(int i=Ans;i;i=Lf[i]) Have[i]=1;
    printf("%d\n",F[Ans]+n-Ans);
    for(int i=1;i<=n;i++)
        if(!Have[i]) printf("%d ",i);
}

E.Assiut Chess

題目連結

Assiut Chess

簡要題解

這是一道構造互動題,要求我們在一個\(8*8\)的棋盤內,控制皇后的移動,在\(130\)步之內使得國王無法移動。
此處提供一種構造思路:
由於棋盤不大,一個很自然的想法就是把這個棋盤掃一遍,慢慢把國王逼到角落裡。
既然要遍歷整個棋盤,那麼不如把皇后放在\((1,1)\),然後慢慢往下挪。
假設皇后在第\(i\)行,國王在皇后下方,皇后想走到\(i+1\)行,把國王繼續往下逼,那麼當皇后往下走時,國王就不能在第\(i+1\)行。
否則皇后往下走之後,國王可以立刻往上走,走到皇后的上方。

我們發現,如果國王在皇后下方,且國王往下走了一步,那麼此時一定滿足皇后往下走的條件,皇后就可以直接往下走。
否則,我們需要把國王趕出第\(i+1\)

行。
實際上,只要皇后把第\(i\)行全部遍歷一遍,並且國王沒有往上走,那麼國王就一定不在第\(i+1\)行,如果國王往上走了,皇后就得重新遍歷這一行。
但是國王往上走的次數是有限的,因為國王一旦往下走,皇后就可以跟著往下走,於是保證了皇后的移動次數。
第一行的時候需要判斷一下,因為國王有可能就在第一行。
如果是這種情況的話,國王第一步一定會往下走,只要皇后不跟著往下走就好了。

程式碼如下:

#include<bits/stdc++.h>
using namespace std;
char Str[30];
int T,Nh,Nl,W,Vs,Col;
int Vis[30];
int main()
{   for(scanf("%d",&T);T;T--)
    {   Nh=1,Nl=1,W=1,Col++,Vs=0;
        printf("%d %d\n",Nh,Nl),fflush(stdout);
        Vs+=Vis[Nl]!=Col,Vis[Nl]=Col,scanf("%s",Str);
        if(strstr(Str,"Done")!=NULL) continue ;
        printf("%d %d\n",Nh,++Nl),fflush(stdout);
        Vs+=Vis[Nl]!=Col,Vis[Nl]=Col;
        while(scanf("%s",Str))
        {   if(strstr(Str,"Done")!=NULL) break ;
            if(strstr(Str,"Up")!=NULL) Col++,Vs=0;
            if(strstr(Str,"Down")!=NULL||Vs==8) Col++,Nh++,Vs=0;
            else Nl+=W,W*=(Nl==1||Nl==8?-1:1);
            printf("%d %d\n",Nh,Nl),fflush(stdout);
            Vs+=Vis[Nl]!=Col,Vis[Nl]=Col;
        }
    }
}