分析:
题解又在卖萌了。。要用RMQ?。。。估计他自己没想清楚吧。。。这题哪用得着RMQ啊。。。
首先,可以暴力枚举循环节的长度:
然后,对每种字母进行判断,求出每个循环节中B,C最靠前的位置和A,B最靠后的位置。
如果A最靠后的位置在B最靠前的后面,或者B最靠后的位置在C最靠前的后面。就说明当前这个循环节长度是矛盾的。
如果不矛盾,则将A最靠后的位置及以前,都设为A,B最靠后的位置及以前(除去A的部分)都设为B,C则填充这个循环节剩余的位置。就能保证是当前长度循环节中,答案字典序最小一种方案。
问题就在于如何处理:“每个循环节中B,C最靠前的位置和A,B最靠后的位置”
可以利用调和级数:暴力枚举每个循环节,O(1)求出当前循环节最靠前的B、C,最靠后的A、B。总的时间复杂度就是
O(N log N)
O
(
N
l
o
g
N
)
。
题解的方法说要用RMQ求这个问题。。。
其实就用前缀和后缀的思想不就行了?
对于每个点,它前面最近的一个A、B字母的位置,以及它后面最靠近的一个B、C字母的位置。这两个东西可以O(N)递推求出来
询问某个循环节时,求出其左端点及以后,最靠前的B、C,以及其右端点及以前最靠后的A、B即可(就是上面维护的那两个东西)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 10000
using namespace std;
int a[MAXN+10],pre[MAXN+10][4],las[MAXN+10][4],ans[4],ans1[4];
char s[20];
int get_pre(int len,int x){
int res=len;
for(int i=1;i<=MAXN;i+=len){
int t=las[i][x];
if(t>=i+len)
continue;
res=min(res,t-i+1);
}
return res;
}
int get_las(int len,int x){
int res=0;
for(int i=1;i<=MAXN;i+=len){
int top=min(i+len-1,MAXN);
int t=pre[top][x];
if(t<i)
continue;
res=max(res,t-i+1);
}
return res;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("wa.out","w",stdout);
int t;
SF("%d",&t);
while(t--){
int m;
SF("%d",&m);
int pos;
ans[1]=ans[2]=ans[3]=-1;
memset(a,0,sizeof a);
memset(pre,0,sizeof pre);
memset(las,0x3f3f3f3f,sizeof las);
for(int i=1;i<=m;i++){
SF("%d",&pos);
SF("%s",s);
if(s[0]=='A')
a[pos]=1;
else if(s[0]=='B')
a[pos]=2;
else
a[pos]=3;
}
for(int i=1;i<=MAXN;i++)
for(int j=1;j<=3;j++){
if(a[i]==j)
pre[i][j]=i;
pre[i][j]=max(pre[i][j],pre[i-1][j]);
}
for(int i=MAXN;i>=1;i--)
for(int j=1;j<=3;j++){
if(a[i]==j)
las[i][j]=i;
las[i][j]=min(las[i][j],las[i+1][j]);
}
for(int len=3;len<=MAXN;len++){
int lasx=0;
bool flag=0;
for(int j=1;j<=3;j++){
if(j!=1){
int prex=get_pre(len,j);
if(prex<=lasx){
flag=1;
break;
}
}
if(j!=3){
lasx=max(lasx+1,get_las(len,j));
ans1[j]=lasx;
}
else
ans1[j]=len;
}
if(flag==0){
for(int j=1;j<=3;j++){
if(ans1[j]<ans[j]||ans[j]==-1){
ans[1]=ans1[1];
ans[2]=ans1[2];
ans[3]=ans1[3];
break;
}
if(ans1[j]>ans[j])
break;
}
}
}
if(ans[1]!=-1)
PF("%d %d %d\n",ans[1],ans[2]-ans[1],ans[3]-ans[2]);
else
PF("NO\n");
}
}