CF590E Birthday
题意
给定 \(n\) 个只由 \(a,b\) 组成的字符串,保证两两不同。
要求从中选出尽可能多的字符串,使得选出的字符串中,任意一个字符串不是另一个的子串。
求最多能选多少并输出一个可行解。
\(n \leq 750, \sum |S_i| \leq 10^6\)
思路
考虑根据包含关系建边,可以得到一张有向无环图,之后我们要求的是一个最大点集,两两不能到达。这就是祭祀。
即求最长反链,然后转化为最小链覆盖,再到传递闭包后的最大匹配,关于证明,ta没了。
接着来说一下如何输出方案。
在求完传递闭包之后,能互相到达的点一定是相邻的,所以最长反链即为最大独立集。
定义:最小点覆盖就是选择最少的点来覆盖所有的边。一个点能覆盖以它为端点的边
最大独立集=所有顶点数-最小点覆盖
突然写不下去了,就看这个吧偷懒,啥时候空了我来补
然后来考虑怎么建图。
由于是子串问题,很容易想到用AC自动机。
但是如果把所有包含关系都求出来,妥妥的T了
对于三个串 \(a,b,c\),如果 \(a\)包含\(b\) , \(b\)包含\(c\), 那么\(a\)包含\(c\)。
回忆AC自动机匹配的过程,对于文本串\(i\)每匹配到一个字符位置\(u\)就跳\(fail\),考虑到每次第一个跳到的一定是最长的,且每次跳的都是前面所有的子串,所以我们只要将\(i\)与\(fail[u]\)上包含的最长完整串连边就行。注意\(fail[u]\)上可能不存在完整串末尾节点,那么我们就继承离他最近的\(fail\)树祖先上的串。
不要忘记\(u\)本身是末尾节点的情况。
至于怎么继承,只要在\(get \_ fail\)中加一句就好了
#include <bits/stdc++.h>
using std::string;
using std::queue;
queue <int> q;
const int N=755,M=10000005;
string s[N];
int ch[M][2],val[M],fail[M],cnt,n,vis[N],now,to[N],len[N],f[N];
int tagl[N],tagr[N];
bool a[N][N];
void insert(string s,int l,int id){
int u=0;
for (int i=0;i<l;i++){
int c=s[i]-'a';
if (!ch[u][c]) ch[u][c]=++cnt;
u=ch[u][c];
}
val[u]=id;
}
void get_fail(){
if (ch[0][0]) q.push(ch[0][0]);
if (ch[0][1]) q.push(ch[0][1]);
while (!q.empty()){
int x=q.front();
if (!val[x]) val[x]=val[fail[x]];
q.pop();
for (int i=0;i<2;i++)
if (ch[x][i]) {
q.push(ch[x][i]);
fail[ch[x][i]]=ch[fail[x]][i];
}else ch[x][i]=ch[fail[x]][i];
}
}
void match(string s,int l,int id){
int u=0;
for (int i=0;i<l;i++){
int c=s[i]-'a';
u=ch[u][c];
if (val[u]) a[id][val[u]]=1;
if (val[fail[u]]) a[id][val[fail[u]]]=1;
}
}
int dfs(int x){
for (int i=1;i<=n;i++)
if (a[x][i] && vis[i]!=now){
vis[i]=now;
if (f[i]==0 || dfs(f[i])){
to[x]=i,f[i]=x;
return 1;
}
}
return 0;
}
void dfs2(int x){
tagl[x]=1;
for (int i=1;i<=n;i++){
if (a[x][i]==0) continue;
if (!tagr[i]){
tagr[i]=1;
dfs2(f[i]);
}
}
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
std::cin>>s[i];
len[i]=s[i].length();
insert(s[i],len[i],i);
}
get_fail();
for (int i=1;i<=n;i++)
match(s[i],len[i],i);
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=a[i][j]|(a[i][k] & a[k][j]);
for (int i=1;i<=n;i++) a[i][i]=0;
int ans=0;
for (int i=1;i<=n;i++)
now=i,ans+=dfs(i);
printf("%d\n",n-ans);
for (int i=1;i<=n;i++)
if (!to[i]) dfs2(i);
for (int i=1;i<=n;i++)
if (tagr[i]==0 && tagl[i]) printf("%d ",i);
}
后记
追随神仙的脚步
争取做到\(\frac{1}{1 page}\)
CF590E Birthday的更多相关文章
- IOI 2020 集训队作业胡扯
首先安慰自己:做的没集训队快很正常-- 很正常-- 做不完也很正常-- 很正常-- 全都不会做也很正常-- 很正常-- 表格 试题一 完成情况 试题二 完成情况 试题三 完成情况 cf549E cf6 ...
- IOI 2020 国家集训队作业
\(\checkmark\) 试题一 完成情况 试题二 完成情况 试题三 完成情况 cf549E cf674G arc103_f \(\checkmark\) cf594E agc034_f agc0 ...
随机推荐
- VS.NET(C#)--2.9_HTML服务器控件案例
HTML服务区控件案例 UI设计视图 UI源码视图 <>
- WSL 服务自动启动
WSL 服务自动启动 参考 https://zhuanlan.zhihu.com/p/47733615 Windows 启用 WSL 功能支持 > Enable-WindowsOptionalF ...
- SqlServer2008 / SqlServer2012 禁用windows登录,sa忘记密码或密码过期如何登陆
以管理员身份运行cmd 1.cmd 下 停止SqlServer服务,net stop mssqlserver: 2.新建windows账号test,加入administrators组里,授予管理员权 ...
- python绘图之turtle库函数的用法
Turtle库是Python语言中一个很流行的绘制图像的函数库,想象一个小乌龟,在一个横轴为x.纵轴为y的坐标系原点,(0,0)位置开始,它根据一组函数指令的控制,在这个平面坐标系中移动,从而在它爬行 ...
- 将py文件打包到docx
import os class FileDownload: def __init__(self): self.exclude = ['db.sqlite3', 'logs', 'media', 'Pi ...
- template中实现加减乘除数学运算
django模板只提供了加法的filter,没有提供专门的乘法和除法运算:django提供了widthratio的tag用来计算比率,可以变相用于乘法和除法的计算. # 加法{{value|add:1 ...
- 使用angularJS设置复选框的回显状态
思路分析: 在angularJS中,我们可以使用ng-checked="expression()"来设置复选框的状态:当expression()返回true时,该复选框为选择中状态 ...
- phpcms企业站的一些知识
头header.html 尾footer.html 主页用index.html 列表页用list.html 单网页用page.html 内容页用show.html {template "co ...
- bootstrap table分页limit计算pageIndex和pageSize
由于bootstrap table的js无法直接获取pageSize和pageIndex的值,只能通过limit进行计算.
- 爬虫请求库 requests
requests模块 阅读目录 一 介绍 二 基于GET请求 三 基于POST请求 四 响应Response 五 高级用法 一 介绍 #介绍:使用requests可以模拟浏览器的请求,比起之前用到 ...