Codeforces Round #737 (Div. 2) 部分題題解
阿新 • • 發佈:2021-08-12
D.Ezzat and Grid
題目連結
簡要題解
對於這一題,我們不難想出一個\(O(n^2)\)的\(Dp\):設\(F[i]\)表示,使前\(i\)行合法,且留下第\(i\)行的最小代價。
那麼轉移的話,就是列舉一個合法的\(j\),使得第\(i\)行和能夠和第\(j\)行相鄰,並將中間的行全部刪掉,我們有轉移方程:
現在考慮優化這個\(Dp\),移項我們可以得到:
\[F[i]-i+1=Min_j(F[j]-j) \]因此我們只需要對於合法的\(j\),找到最小的\(F[j]-j\)即可。
什麼時候合法呢?當第\(i\)
於是不難想到區間取\(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
題目連結
簡要題解
這是一道構造互動題,要求我們在一個\(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;
}
}
}