Sol
应该存在一种经典技巧吧,就是那种可行性DP通过某种手段(感觉最常见的是贪心)压缩一个状态维度。
看到这个题首先应该有个 \texttt{native} 的想法(基于经验?):考虑设 f_{i,j} 为考虑前 i 个灯笼,能覆盖前缀 [1,j] 的可行性。
然后这里估计能列个方程吧,又或者是贪心的想一想,对于每个 i ,显然我们只关心最大的 j 使得 f_{i,j}=1 。
所以重新考虑状态,设 f_{i}=j 为能覆盖的最大前缀 [1,j] 。
然后转移分讨一下即可。
Code
#include<bits/stdc++.h>
using namespace std;
#define fio(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
#define tio() freopen("in.txt","r",stdin),freopen("out.txt","w",stdout)
using ll=long long;
int T;
int n;
const int N=3e5+10;
int a[N];
int f[N],p[N];
char s[N];
string ans;
struct ST{
int a[N][31],l2[N];
void init(){
for(int i=2;i<=n;++i)l2[i]=l2[i>>1]+1;
for(int j=1;j<31;++j)
for(int i=1;i+(1<<j)-1<=n;++i)
a[i][j]=max(a[i][j-1],a[i+(1<<j-1)][j-1]);
}
int query(int l,int r){
if(r<l)return -1;
int x=l2[r-l+1];
return max(a[l][x],a[r-(1<<x)+1][x]);
}
}st;
int main(){
scanf("%d",&T);
f[0]=f[1]=0;
s[1]='R';
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]),st.a[i][0]=i+a[i];
st.init();
ans="";
for(int i=2;i<=n;++i){
if(i-a[i]>f[i-1]+1){f[i]=f[i-1]; p[i]=i-1; s[i]='R';}
else{
int j=lower_bound(f,f+i,max(0,i-a[i]-1))-f;
f[i]=max(i-1,st.query(j+1,i-1));
int v=-1;
if(f[i-1]>=i)v=max(i+a[i],f[i-1]);
if(v<=f[i]){
p[i]=j;
s[i]='L';
}
else{
f[i]=v;
p[i]=i-1;
s[i]='R';
}
}
}
if(f[n]>=n)printf("YES\n");
else {
printf("NO\n");
continue;
}
// s[n]='L';
for(int i=n,cur=n;i>=1;--i){
if(i==cur)ans=s[i]+ans,cur=p[i];
else ans='R'+ans;
}
cout<<ans<<"\n";
}
return 0;
}