2023NOIP A层联测10 T4 子序列

题面及数据范围

Ps:链接来自accoderOJ。

考场2小时才做完 T1,抱着试一试的心态看了 T4,然后想到做法了,调了 1 个多小时没调除了,赛后发现数组开小了,因为与正解做法稍有不同,于是又调了一下午……

转移方程

设状压dp \(f[i][state][0/1]\) 是到第 \(i\) 个字母,\(state\) 中有两位标位了 \(1\) ,不妨设标 \(1\) 的是第 \(x\) 位和第 \(y\) 位表示使用了第 \(x\) 个和第 \(y\) 个字母(\(x<y\)),\(0\) 表示是 第 \(x\) 个字母是上一个字母,\(1\) 表示是第 \(y\) 个是上一个字母。

那么不难得到有如下转移

若 \(s[i]\) 是第 \(x\) 个字母:

\[f[i][state][0]=f[i-1][state][1]+1
\]

若 \(s[i]\) 是第 \(y\) 个字母:

\[f[i][state][1]=f[i-1][state][0]+1
\]

若 \(s[i]\) 都不是:

\[f[i][state][1]=f[i-1][state][1]\\
f[i][state][0]=f[i-1][state][0]
\]

统计答案

那我们要怎么样统计答案呢?

可以想到用 \(l-1\) 位置时与 \(r\) 时的同一个状态 \(state\) 作差求出最大长度。

观察一手性质,发现对于每一个状态而言,最靠近 \(l\) 的那个字母做开头一定可以得到最长,而结尾两者皆可,于是有:

若靠近的是第一个字母:

\[ans[i]=\max(f[r[i]][state][1],f[r[i]][state][0])-f[l[i]-1][state][1]
\]

Ps:第一个字母前面肯定接的是第二个字母。

若靠近的是第二个字母:

\[ans[i]=\max(f[r[i]][state][1],f[r[i]][state][0])-f[l[i]-1][state][0]
\]

预计时间复杂度:\(O(2^{26}n)\)

优化

这种复杂度暴力都心累,于是乎我们要优化。

dp 优化

先使用滚动优化,优化掉第 \(1\) 维,那么转移方程变成:

若 \(s[i]\) 是第 \(x\) 个字母:

\[f[state][0]=f[state][1]+1
\]

若 \(s[i]\) 是第 \(y\) 个字母:

\[f[state][1]=f[state][0]+1
\]

若 \(s[i]\) 都不是:

\[f[state][1]=f[state][1]\\
f[state][0]=f[state][0]
\]

由于 \(state\) 有效状态为 \(26\) 个字母中选 \(2\) 个字母不重复的状态,那么我们可以给每一个编号表示一个状态,即表示一种两个字母的方案,这样的编号有 \(C^2_{26}=325\) 个。

而且当前 \(s[i]\) 已经确定,相当于已经确定了 \(1\) 个字母,所以转移时枚举另外一个字母组成答案即可。

dp 时间复杂度优化至 \(T(25n)\)。

统计优化

但这样子我们同时也需要记录在 \(l-1\) 与 \(r\) 位置时 \(f\) 数组的值,便于后面进行答案统计。

统计答案是可以将每一个查询按 \(l\) 大小排序,将同个字母的位置从小到大压入队列中。

维护每个队列顶 \(\geqslant l\)。

每次通过 \(l\) 查询每个状态最近字母时,该字母堆顶就是答案。

维护堆总时间负载度 \(O(n)\),维护均摊 \(O(1)\),查询 \(O(1)\)。

统计答案时间复杂度 \(T(C^2_{26}n)\)。

但我们(也可能只有我)的空间复杂度较高,记录数组大小高达 \(2*10^5*325*2=1.3*10^8\)。

考虑进行优化,发现对于 \(r\) 而言,只关心 \(0/1\) 中的最大值,于是记录 \(r\) 端点的数组只需要记录最大值即可。

将 \(r\) 的记录数组与 \(l\) 的剥离开,减小了 \(\frac{1}{4}\) 的空间,由于基数大,于是多出了 \(4*10^7\) 的数组空间可以使用。

代码仅供参考:

#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);
}

2023NOIP A层联测10 T4 子序列的更多相关文章

  1. Java面试之持久层(10)

    91,什么是ORM?         对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术: 简单的说,O ...

  2. 如何从代码层防御10大安全威胁中的 Xpath Injection?

    普遍性和可检测性: Xpath 注入是 OWASP TOP10 安全威胁中 A1 Injection 中的一种,注入漏洞发生在应用程序将不可信的数据发送到解释器时.虽然注入漏洞很容易通过审查代码发现, ...

  3. 《细说PHP》第四版 样章 第18章 数据库抽象层PDO 10

    18.8  设计完美分页类 数据记录列表几乎出现在Web项目的每个模块中,假设一张表中有十几万条记录,我们不可能一次全都显示出来,当然也不能仅显示几十条.为了解决这样的矛盾,通常在读取时设置以分页的形 ...

  4. CSharpGL(12)用T4模板生成CSSL及其renderer代码

    CSharpGL(12)用T4模板生成CSSL及其renderer代码 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立 ...

  5. 使用T4模板生成代码的学习

    之前做项目使用的都是Db First,直接在项目中添加Entity Framework,使用T4模板(T4模板引擎之基础入门)生成DAL BLL层等(T4模板是一个同事给的,也没有仔细研究,代码如下: ...

  6. C# Winform 实现自定义半透明loading加载遮罩层

    在网页中通过div+css实现半透明效果不难,今天我们看看一种在winfrom中实现的方法: 效果图如下,正常时: 显示遮罩层时: 自定义遮罩层控件的源码如下: View Row Code 1 usi ...

  7. 用标准3层神经网络实现MNIST识别

    一.MINIST数据集下载 1.https://pjreddie.com/projects/mnist-in-csv/      此网站提供了mnist_train.csv和mnist_test.cs ...

  8. tensorFlow(四)浅层神经网络

    tensorFlow见基础 实验 MNIST数据集介绍 MNIST是一个手写阿拉伯数字的数据集. 其中包含有60000个已经标注了的训练集,还有10000个用于测试的测试集. 本次实验的任务就是通过手 ...

  9. 可变多隐层神经网络的python实现

    说明:这是我对网上代码的改写版本,目的是使它跟前一篇提到的使用方法尽量一致,用起来更直观些. 此神经网络有两个特点: 1.灵活性 非常灵活,隐藏层的数目是可以设置的,隐藏层的激活函数也是可以设置的 2 ...

  10. C# Winform 实现自定义半透明遮罩层介绍

    在网页中通过div+css实现半透明效果不难,今天我们看看一种在winfrom中实现的方法: 效果图如下,正常时: 显示遮罩层时: 自定义遮罩层控件的源码如下: View Row Code 1 usi ...

随机推荐

  1. 为 OpenWrt 路由器编译 minieap-sysu 项目

    学校的校园网要使用锐捷认证,于是想把认证客户端装到刷了 OpenWrt 的路由器上面.然而认证客户端 Linux 版只支持 x86 架构,我的路由器处理器却是 AArch64 架构,装不了,所以只能想 ...

  2. 基于PaddleNLP信息抽取,uie微调打造自己专属的信息抽取模型

    基于PaddleNLP信息抽取,uie微调打造自己专属的信息抽取模型 UIE模型简介 UIE优势 应用示例 UIE开箱即用 UIE适用抽取示例 命名实体识别(Named Entity Recognit ...

  3. release版本 APP 出现waiting for debugger

    前提:同一包代码,打包两个版本,修改包名,在同一设备上存在两个版本 状态:运行时,发现 一版本是正常 另一版本打开会提示 Waiting for debugger弹窗,(此时由于是开发状态,设备一直开 ...

  4. el-popover - 问题

    背景:elemet - ui和vue , el-table中使用了 el-popover , el-popover 中使用了form, 每编辑一行数据,点击编辑按钮,出现el-popover弹窗,页面 ...

  5. vue3 如何获取 dom

    1. 通过 ref     1. 在 html 标签上指定 ref 属性     2. 在 setup 中定义并返回.注意:标签上的 ref 属性名需要跟 setup 中的对应 <h1 ref= ...

  6. C# 泛型对象和DataTable之间的相互转换

    应用场景 实际开发场景下会经常出现DataTable和List对象需要相互转换的时候,通过方法提取避免重复造轮子 List转换成DataTable 基本思路: 向DataTable里面添加新的数据内容 ...

  7. PDF解析,还能做得更好

    随着大模型文档智能应用逐渐步入正轨,文档解析类产品成为其中重要的一环.文档解析工具能够"唤醒"沉睡在PDF文件中的知识,将其转化为机器能够识别.读取的信息,将可用数据从txt.cs ...

  8. Parquet.Net: 将 Apache Parquet 移植到 .NET

    Parquet.Net 是一个用于读取和写入 Apache Parquet 文件的纯 .NET 库,使用MIT协议开源,github仓库:https://github.com/aloneguid/pa ...

  9. 运输小猫娘之再续 5k 传奇

    写的比较意识流 前情提要 上回书说到,5k 因为拯救大家被炸断了 \(1000000007\) 米的牛至中的十五千米,尽管大家的欢呼声如此热烈,就像大家的热量正在像烈火一样散发出来,但是 5k 却无心 ...

  10. 标准库之 random 模块

    一.介绍random模块 1.1.random模块简介 random模块是Python标准库中用于生成伪随机数的模块,伪随机数是通过算法生成的数列,在一定范围内表现出随机性,虽然这些数列在一定程度上是 ...