寿司
考场上我对于这道题第一眼感觉是DP(反正不会是数据结构),但n的数据范围太大了,我没有想到O(n)的DP。于是考虑是否是贪心,但考场上我推出的贪心式子有问题。我是通过枚举每一个连续位置,找出到达这个位置的步数,求步数的最小值,但我的贪心方法在找到达连续位置的步数时不是最优,所以错了。
综上,我与正解的差距过大,只有10pts。以下是正解的做法,因为是环上的问题,所以我们先把这个问题拆成链上的问题。在链上,我们很容易想到对于一个B的移动步数是向左移的步数和向右移的步数中较优的一个,所以设pos=(B的个数+1)/2,在pos左边的向左移,在pos右边的向右移,这样产生的步数一定是最优。
我们把上面的结论换到环上,因为环是循环结构,所以我们把给定的字符串复制一遍接在后面,预处理长为n*2的字符串的前缀和(分别为在前i个字符中R的个数/(l1_{i}/),在前i个字符中B的个数/(l2_{i}/),在后i个字符中R的个数/(r1_{i}/),在后i个字符中B的个数/(r2_{i}/))
我们先处理以第一个字符开头长为n的字符串,pos=(/(l2_{n}+1/))/2,然后按照链上的方法做,但因为pos会移动,需要记录第pos个B在字符串中的位置。在后面的字符串中,如果pos不移动则左边所有R的贡献减一,右边所有R贡献加一;如果移动pos下一个B就是pos,我们可以通过减去右边的B的数量,加上左边的B的数量来维护R的变动和当前状态的答案。最终答案取最小值。
AC Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
const int N=2e6+5;
int t,n,pos;
ll ans,res;
ll l1[N],l2[N],r1[N],r2[N];
char s[N];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%s",s+1);
n=strlen(s+1);
for(re int i=1;i<=(n<<1);++i){
l2[i]=l2[i-1],l1[i]=l1[i-1];
if(s[(i-1)%n+1]=='R') ++l1[i];
else ++l2[i];
}
for(re int i=(n<<1);i;--i){
r2[i]=r2[i+1],r1[i]=r1[i+1];
if(s[(i-1)%n+1]=='R') ++r1[i];
else ++r2[i];
}
pos=(l2[n]+1)>>1;
int p=-1;res=0;
for(re int i=1;i<=n;++i){
if(l2[i]==pos){
p=i;
for(re int j=n;j>=i;--j)
if(s[j]=='R')
res+=0ll+r2[j]-r2[n+1];
break;
}
if(s[i]=='R')
res+=0ll+l2[i];
}
re int l=1,r=n;
ans=res;
while(l<=n){
if(s[(l-1)%n+1]=='B'){
res-=0ll+l1[p]-l1[l-1];
res+=0ll+r1[p]-r1[++r];
++l;
while(s[(++p-1)%n+1]=='R'){
res+=0ll+l2[p]-l2[l-1];
res-=0ll+r2[p]-r2[r+1];
}
}
else ++l,++r;
ans=min(ans,res);
}
printf("%lld/n",ans);
}
return 0;
}
最优序列
考场上我想到了DP,但没有想到状压DP。其实从/(m,k /leq 10/),就基本上可以猜到是状压DP,但因为对数据不敏感,我没有使用状压DP的意识,考场上我用了单调队列优化DP,过了大样例后就没管了,因此只有20pts
正解是用状压DP,先预处理出(1<<m)-1中可行的状态
岛屿
是一道基环树的模板题
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/282132.html