2023NOIP A层联测10 T4 子序列
2023NOIP A层联测10 T4 子序列
Ps:链接来自accoderOJ。
考场2小时才做完 T1,抱着试一试的心态看了 T4,然后想到做法了,调了 1 个多小时没调除了,赛后发现数组开小了,因为与正解做法稍有不同,于是又调了一下午……
转移方程
设状压dp
那么不难得到有如下转移
若
若
若
统计答案
那我们要怎么样统计答案呢?
可以想到用
观察一手性质,发现对于每一个状态而言,最靠近
若靠近的是第一个字母:
Ps:第一个字母前面肯定接的是第二个字母。
若靠近的是第二个字母:
预计时间复杂度:
优化
这种复杂度暴力都心累,于是乎我们要优化。
dp 优化
先使用滚动优化,优化掉第
若
若
若
由于
而且当前
dp 时间复杂度优化至
统计优化
但这样子我们同时也需要记录在
统计答案是可以将每一个查询按
维护每个队列顶
每次通过
维护堆总时间负载度
统计答案时间复杂度
但我们(也可能只有我)的空间复杂度较高,记录数组大小高达
考虑进行优化,发现对于
将
代码仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define F first
#define S second
const int maxn=3*5e5+5,maxm=13*25+5,maxz=1e5+5;
struct node
{
int l,r,id;
}t[maxz];
struct ANS
{
int val;
pair<char,char>c;
}Ans[maxz];
int m,n,cnt,ctl,ctr;
int f[maxm][2],gt[maxz][maxm],g[maxz][maxm][2],mp[2627],fpl[maxn],fpr[maxn];
//gt 为 r 的统计数组,g 为 l 的统计数组,mp 是 二元组的第一位*100+第二位 后得出的编号
bool vis[maxn];//记录该位置是不是左端点
bool cis[maxn];//记录该位置是不是右端点
pair<char,char>p[maxm];//编号对应的一组字母
char s[maxn];
queue<int>st[27];
bool cmp(node a,node b){return a.l<b.l;}
inline int chs(char a,char b){return (a-'a'+1)*100+(b-'a'+1);}
inline void ycl()//预处理二元组编号及编号对应二元组
{
for(char i='a';i<='z';i++)
{
for(char j=i+1;j<='z';j++)
{
if(j==i) continue;
p[++cnt]=make_pair(i,j);
mp[chs(i,j)]=cnt;
mp[chs(j,i)]=cnt;
}
}
}
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
cin>>s+1;
n=strlen(s+1);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&t[i].l,&t[i].r);
t[i].id=i;
vis[t[i].l-1]=1;
cis[t[i].r]=1;
}
sort(t+1,t+m+1,cmp);
for(int i=1;i<=n;i++) st[s[i]-'a'+1].push(i);
for(int i=1;i<=26;i++) st[i].push(n+1);//队列优化统计
ycl();
for(int i=1;i<=n;i++)
{
for(int j='a';j<='z';j++)//转移
{
if(j==s[i]) continue;
pair<char,char>k=make_pair(s[i],j);
if(k.F>k.S) swap(k.F,k.S);
if(k.F==s[i]) f[mp[chs(k.F,k.S)]][0]=f[mp[chs(k.F,k.S)]][1]+1;
else f[mp[chs(k.F,k.S)]][1]=f[mp[chs(k.F,k.S)]][0]+1;
}
if(vis[i])//l 端点记录
{
ctl++;
fpl[i]=ctl;
for(int j=1;j<=cnt;j++) g[ctl][j][0]=f[j][0],g[ctl][j][1]=f[j][1];
}
if(cis[i])//r 端点记录
{
ctr++;
fpr[i]=ctr;
for(int j=1;j<=cnt;j++) gt[ctr][j]=max(f[j][0],f[j][1]);
}
}
for(int i=1;i<=m;i++)//求答案
{
pair<char,char>cans;
cans={0,0};
int ans=0;
for(int j=1;j<=cnt;j++)
{
pair<char,char>x=p[j];
while(st[x.F-'a'+1].front()<t[i].l) st[x.F-'a'+1].pop();//维护队列
while(st[x.S-'a'+1].front()<t[i].l) st[x.S-'a'+1].pop();
int fi=st[x.F-'a'+1].front();//最靠近 l 的字母
int se=st[x.S-'a'+1].front();
if(fi>t[i].r&&se>t[i].r) continue;
if(fi<se)
{
if(ans<gt[ fpr[t[i].r] ][j]-g[ fpl[t[i].l-1] ][j][1])
{
ans=gt[ fpr[t[i].r] ][j]-g[ fpl[t[i].l-1] ][j][1];
cans=x;
}
else if(ans==gt[ fpr[t[i].r] ][j]-g[ fpl[t[i].l-1] ][j][1])
{
if(cans>x) cans=x;
}
}
else if(fi>se)
{
swap(x.F,x.S);
if(ans<gt[ fpr[t[i].r] ][j]-g[ fpl[t[i].l-1] ][j][0])
{
ans=gt[ fpr[t[i].r] ][j]-g[ fpl[t[i].l-1] ][j][0];
cans=x;
}
else if(ans==gt[ fpr[t[i].r] ][j]-g[ fpl[t[i].l-1] ][j][0])
{
if(cans>x) cans=x;
}
}
}
Ans[t[i].id].val=ans;
Ans[t[i].id].c=cans;
}
for(int i=1;i<=m;i++) printf("%d %c%c\n",Ans[i].val,Ans[i].c.F,Ans[i].c.S);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现