https://codeforces.com/gym/101669/problem/E
猜了一下结论然而过了,有点惊喜。。。
首先把12种基调状态压缩预处理一下,然后高位前缀和维护一下(upd:之前写错了)子集,vis[mask]=true表示当前有的音调状态是存在某一种基调中的
然后我们知道给出的是环,所以我们把他拓展到2倍,然后找dp[i]-dp[i-n]就是截取这一段的最小值求出来就行了,求最小值就是i向后移动,然后mask更新,如果vis[mask]不行了,就说明[l,i]这一段不能再同一首歌中,所以l就要向右移动直到[l,i]可以在同一首歌中。
然后有个问题,如果dp[i-n]=dp[i-n+1],说明从a[i-n+1]到a[n]的开头是由dp[i-n]拓展过来的还是必须单独开一段,所以要+1,因为我每次都是直到某一个不行才换新的一段。其实这里我没想清楚,也证明不出,因为舍弃掉a[i-n]时,从a[i-n+1]开始可能可以向右到更右边,dp[i]可能会更小一些,但我怀疑这12种基调会完美地解决这个问题,就是舍掉一个向右走,还是一样会换新的一段,然后就过了233
用scanf超时了了一发,换了fread只要400ms,看wxh的博客发现他用cin取消同步读string,我试了一下我的变成了1000ms还是过了
wxh的做法更对一些,不用考虑12种基调的具体情况。只需要枚举开头是12种情况的哪一种,然后尽可能左右扩大以后,跑剩下的部分,复杂度变成了O(12*n)但是非常正确
#include<bits/stdc++.h>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(int &x){
char c=nc(),f=1;
for(;(c<'0'||c>'9')&&c!=EOF;c=nc()) if(c=='-') f=-1;
if(c==EOF) return EOF;
for(x=0;c>='0'&&c<='9';c=nc()) x=x*10+c-'0';
x*=f;
return 1;
}
inline int blank(char ch){
return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
}
inline int rd(char *s){
char c=nc();
while(blank(c)) c=nc();
if(c==EOF) return EOF;
for (;!blank(c)&&c!=EOF;c=nc())*s++=c;
*s=0;
return 1;
}
const int maxl=2e7+10;
int n,ans;
int a[maxl],dp[maxl];
bool vis[maxl];
char s[100];
int tx[12]={0,2,4,5,7,9,11,12};
int num[12];
inline void prework()
{
//scanf("%d",&n);
rd(n);
int l;
for(int i=1;i<=n;i++)
{
//scanf("%s",s);
rd(s);
l=strlen(s);
if(s[0]=='D')
a[i]= (l==2)?0:1;
if(s[0]=='R')
a[i]= (l==2)?2:3;
if(s[0]=='M')
a[i]=4;
if(s[0]=='F')
a[i]= (l==2)?5:6;
if(s[0]=='S' && s[1]=='o')
a[i]= (l==3)?7:8;
if(s[0]=='L')
a[i]= (l==2)?9:10;
if(s[0]=='S' && s[1]=='i')
a[i]=11;
}
for(int i=n+1;i<=2*n;i++)
a[i]=a[i-n];
int mask,d;
for(int i=0;i<12;i++)
{
mask=1<<i;
for(int j=1;j<=7;j++)
{
d=(i+tx[j])%12;
mask|=1<<d;
}
vis[mask]=true;
}
for(int j=0;j<12;j++)
for(int i=0;i<(1<<12);i++)
if(!(i&(1<<j)))
vis[i]|=vis[i|(1<<j)];
}
inline void mainwork()
{
int mask=0,l=1;
for(register int i=1;i<=2*n;++i)
{
if(num[a[i]]==0)
mask|=1<<a[i];
num[a[i]]++;
while(!vis[mask] || l<=i-n)
{
num[a[l]]--;
if(!num[a[l]])
mask^=1<<a[l];
l++;
}
dp[i]=dp[l-1]+1;
}
ans=dp[n];
for(register int i=n+1;i<=2*n;++i)
{
if(dp[i-n]==dp[i-n+1])
ans=min(ans,dp[i]-dp[i-n]+1);
else
ans=min(ans,dp[i]-dp[i-n]);
}
}
inline void print()
{
printf("%d",ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}