My Blogs
[ARC177F] Two Airlines
有點魔怔的題。
一個基本的觀察是如果當前某個人 \(A\) 拿著盒子走到了位置 \(i\),那位置小於 \(i\) 的人一定永遠沒用了。如果之後要用到前面的人 \(B\),就應當讓 \(B\) 拿著盒子走到 \(i\) 而不是讓 \(A\),這樣 \(A\) 待在原來的位置,代價一定不會更劣。
再手玩一下,可以發現每次的過程都是:某個人 \(A\) 拿起盒子,走到某個地方。然後後面來了一個和 \(A\) 顏色不一樣的人(或者是本身就在這個地方的人),走到了 \(A\) 現在的位置,然後帶著箱子走了,此時 \(A\) 也已經沒有用了。
進一步的,“後面來人”的這個過程,對於每種顏色內部,一定是先用座標儘量小的人來。這個結論不難用調整法證明。
而最優解一定能用上述過程刻畫出來。所以可以設 \(f_{i,j,k,0/1}\) 表示當前盒子在 \(i\),被 \(0/1\) 顏色的人拿著,\(i\) 後面的 \(j\) 個顏色為 \(0\) 的人已經用過了(此時在 \(<i\) 的位置),\(k\) 個顏色為 \(1\) 的人已經用過了。轉移 \(\mathcal O(1)\),狀態數和複雜度都是 \(\mathcal O(n^3)\) 的。
接下來的一個想法觀察性質,能不能簡化一維狀態什麼的,但是好像不太好做。除此之外比較自然地,可以感受到 \(j,k\) 都不會太大,設成 \(20\) 左右,交上去就直接過了。下面證明 \(j,k\) 都只需要取到 \(\log n\)。
直觀感受一下,“後面來人”的原因一定是,\(i\) 後面有一段比較長的和當前拿著箱子的顏色不同的路。
紅色和藍色的塊分別表示序列上紅色和藍色的連續位置。紅色段裡面不一定全部都是紅色,藍色段裡也不一定全是藍色。紅色和藍色的線條表示人走過的路程。\(a_i\) 表示這個藍色段裡面有多少個藍色格子。
稱除去搬運盒子過程中造成的代價為“額外代價”。因為第二條紅色線條走到他的目標位置(第二個紅塊)額外花費了 \(\sum_{j\geq 2}a_j\) 的代價,所以 \(a_1>\sum_{j\geq 2}a_j\),否則可以讓第一個人直接走到第二個紅色段:
這樣一定不劣。後面的同理,\(a_i>\sum_{j>i}a_j\),所以最多 \(\log\) 輪之後 \(a_i\) 就要超出值域了。所以 \(j,k\) 的維度都只需要開到 \(20\)。預處理一下還是能做到 \(\mathcal O(1)\) 轉移。總複雜度 \(\mathcal O(n\log^2 n)\)。
int n,m,f[60010][21][21][2],nex[2][60010][21],t[60010][2],a[60010][2];
char s[60010];
vi ve[2];
inline void mian()
{
read(n,m),read(s);int x,ans=INF;char ch;
while(m--)read(x),read(ch),t[x][ch=='A']++,ve[ch=='A'].eb(x);
for(int i=1;i<=n;++i)a[i][0]=a[i-1][0],a[i][1]=a[i-1][1],++a[i][s[i]=='A'];
sort(ve[0].begin(),ve[0].end()),sort(ve[1].begin(),ve[1].end());
for(int k=0;k<2;++k)for(int i=0;i<=n;++i)
{
int p=lower_bound(ve[k].begin(),ve[k].end(),i)-ve[k].begin();
for(int j=1;j<=20;++j)
if(p+j-1<(int)ve[k].size())nex[k][i][j]=ve[k][p+j-1];
else nex[k][i][j]=-1;
}
memset(f,127,sizeof(f));
if(nex[0][0][1]!=-1)f[0][1][0][0]=a[nex[0][0][1]][1];
if(nex[1][0][1]!=-1)f[0][0][1][1]=a[nex[1][0][1]][0];
for(int i=0;i<n;++i)
{
for(int j=0;j<=20;++j)for(int k=0;k<=20;++k)
{
if(j<20&&nex[0][i][j+1]!=-1)Mmin(f[i][j+1][k][0],f[i][j][k][1]+a[nex[0][i][j+1]][1]-a[i][1]);
if(k<20&&nex[1][i][k+1]!=-1)Mmin(f[i][j][k+1][1],f[i][j][k][0]+a[nex[1][i][k+1]][0]-a[i][0]);
}
for(int j=0;j<=20;++j)for(int k=0;k<=20;++k)for(int l=0;l<2;++l)
Mmin(f[i+1][max(0ll,j-t[i][0])][max(0ll,k-t[i][1])][l],f[i][j][k][l]+(l!=(s[i+1]=='A')));
}
for(int i=0;i<=20;++i)for(int j=0;j<=20;++j)for(int k=0;k<2;++k)Mmin(ans,f[n][i][j][k]);
write(ans);
}