Codeforces 590E - Birthday(AC 自动机+Dilworth 定理+二分图匹配)
AC 自动机有时只是辅助建图的工具,真的
首先看到多串问题,果断建出 AC 自动机。设 \(m=\sum|s_i|\)。
不难发现子串的包含关系构成了一个偏序集,于是我们考虑转化为图论,若 \(s_j\) 包含于 \(s_i\) 则连一条 \(i\to j\) 的边。显然利用 AC 自动机可实现 \(\mathcal O(m)\) 建图。
题目要我们求的实际上是该偏序集的最大反链大小,根据 Dilworth 定理可将其转化为最小可相交覆盖的大小。
而最小可相交链覆盖的大小又可以通过传递闭包转化为最小不可相交链覆盖的问题,最小不可相交问题又可通过拆点二分图求出。故第一问答案就是 \(n-\) 拆点二分图最大匹配,这个想怎么搞怎么搞,网络流、匈牙利皆可(然鹅 wtcl 不会匈牙利只好跑网络流了)。
至于输出方案……这个嘛,考虑我们当时求最小边覆盖是如何构造方案的,就一遍 DFS 求出源点能到达的点,那么最小边覆盖就是二分图左部不能到达的点 \(+\) 二分图右部能到达的点。最大独立集就求个补集就行了。
值得注意的一点是此题 \(m\) 高达 \(10^7\),递归显然会爆栈,故不能通过建出 fail 树并在 fail 树上一遍 DFS 实现建图。考虑在求 fail 数组的时候再记录一个 \(pos_i\) 表示 \(i\) 在 fail 树的祖先中离它最近的是某个串结尾位置的节点,建图的时候就枚举字符串 \(s_i\) 并遍历根到 \(s_i\) 结尾位置的路径上所有点,若发现某个点的 \(pos\) 值非零就连一条 \(i\to pos_x\) 的边,如果 \(fail_i\) 的 \(pos\) 值非零那也连一条 \(i\to pos_{fail_i}\) 的边,再 \(n^3\) 求遍传递闭包即可建出图来,正确性显然,并且巧妙地避开了递归爆栈的问题。
代码(荣 膺 最 劣 解):
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=750;
const int MAXLEN=1e7;
const int MAXV=1502;
const int MAXE=1.5e6;
const int INF=0x3f3f3f3f;
int n;string s[MAXN+5];
int ch[MAXLEN+5][2],fail[MAXLEN+5],pos[MAXLEN+5],ncnt=0;
bool d[MAXN+5][MAXN+5];
void insert(string s,int id){
int cur=0;
for(int i=0;i<s.size();i++){
if(!ch[cur][s[i]-'a']) ch[cur][s[i]-'a']=++ncnt;
cur=ch[cur][s[i]-'a'];
} pos[cur]=id;
}
void getfail(){
queue<int> q;
for(int i=0;i<2;i++) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<2;i++){
if(ch[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];q.push(ch[x][i]);
if(!pos[ch[x][i]]) pos[ch[x][i]]=pos[fail[ch[x][i]]];
} else ch[x][i]=ch[fail[x]][i];
}
}
}
int S=1501,T=1502;
int hd[MAXV+5],to[MAXE+5],cap[MAXE+5],nxt[MAXE+5],ec=1;
void adde(int u,int v,int f){
to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec;
to[++ec]=u;cap[ec]=0;nxt[ec]=hd[v];hd[v]=ec;
}
int dep[MAXV+5],now[MAXV+5];
bool getdep(){
memset(dep,-1,sizeof(dep));dep[S]=0;
queue<int> q;q.push(S);now[S]=hd[S];
while(!q.empty()){
int x=q.front();q.pop();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(!~dep[y]&&z){dep[y]=dep[x]+1;now[y]=hd[y];q.push(y);}
}
} return ~dep[T];
}
int getflow(int x,int f){
if(x==T) return f;int ret=0;
for(int &e=now[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z&&dep[y]==dep[x]+1){
int w=getflow(y,min(f-ret,z));
ret+=w;cap[e]-=w;cap[e^1]+=w;
if(f==ret) return ret;
}
} return ret;
}
int dinic(){
int ret=0;
while(getdep()) ret+=getflow(S,INF);
return ret;
}
bool vis[MAXV+5];
void dfs(int x){
if(vis[x]) return;vis[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z) dfs(y);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) cin>>s[i],insert(s[i],i);
getfail();
// for(int i=1;i<=ncnt;i++) printf("%d\n",pos[i]);
for(int i=1;i<=n;i++){
int cur=0;
for(int j=0;j<s[i].size();j++){
if(pos[cur]) d[i][pos[cur]]=1;
cur=ch[cur][s[i][j]-'a'];
} if(pos[fail[cur]]) d[i][pos[fail[cur]]]=1;
}
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
d[i][j]|=d[i][k]&d[k][j];
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
if(d[i][j]&&i!=j) adde(i,j+n,1);
}
for(int i=1;i<=n;i++) adde(S,i,1),adde(i+n,T,1);
printf("%d\n",n-dinic());dfs(S);vector<int> ans;
for(int i=1;i<=n;i++) if(vis[i]&&!vis[i+n]) ans.pb(i);
sort(ans.begin(),ans.end());ffe(it,ans) printf("%d ",*it);
return 0;
}
Codeforces 590E - Birthday(AC 自动机+Dilworth 定理+二分图匹配)的更多相关文章
- [BZOJ1143][CTSC2008]祭祀river(Dilworth定理+二分图匹配)
题意:给你一张n个点的DAG,最大化选择的点数,是点之间两两不可达. 要从Dilworth定理说起. Dilworth定理是定义在偏序集上的,也可以从图论的角度解释.偏序集中两个元素能比较大小,则在图 ...
- Codeforces 163E(ac自动机、树状数组)
要点 显然ac自动机的板子就可以暴力一下答案了 为了优化时间复杂度,考虑套路fail树的dfs序.发现本题需要当前这个尾点加上所有祖先点的个数,考虑使用树状数组差分一下,在父点+1,在子树后-1,每次 ...
- AC自动机——多个kmp匹配
(并不能自动AC) 介绍: Aho-Corasick automaton,最经典的处理多个模式串的匹配问题. 是kmp和字典树的结合. 精髓与灵魂: ①利用trie处理多个模式串 ②引入fail指针. ...
- ac自动机暴力跳fail匹配——hdu5880
很简单的题,ac自动机里再维护一个len表示每个状态的串长,用s去query时每到一个结点都要暴力跳fail,因为有可能这个结点不是,但是其fail是危险结点,找到一个就直接break 再用个差分数组 ...
- Codeforces 739D - Recover a functional graph(二分图匹配)
Codeforces 题面传送门 & 洛谷题面传送门 首先假设我们已经填好了所有问号处的值怎样判断是否存在一个合法的构造方案,显然对于一种方案能够构造出合法的基环内向森林当且仅当: \(\fo ...
- Codeforces 547E - Mike and Friends(AC 自动机+树状数组)
题面传送门 好久每做过 AC 自动机的题了--做几个题回忆一下罢 AC 自动机能够解决多串匹配问题,注意是匹配,碰到前后缀的问题那多半不在 AC 自动机能解决的范围内. 在初学 AC 自动机的时候相信 ...
- AC 自动机
AC自动机(Aho-Corasick Automata)是经典的多模式匹配算法.从前我学过这个算法,但理解的不深刻,现在已经十分不明了了.现在发觉自己对大部分算法的掌握都有问题,决定重写一系列博客把学 ...
- HDU-4518 吉哥系列故事——最终数 AC自动机+数位DP
题意:如果一个数中的某一段是长度大于2的菲波那契数,那么这个数就被定义为F数,前几个F数是13,21,34,55......将这些数字进行编号,a1 = 13, a2 = 21.现给定一个数n,输出和 ...
- UVa 11468 (AC自动机 概率DP) Substring
将K个模板串构成一个AC自动机,那些能匹配到的单词节点都称之为禁止节点. 然后问题就变成了在Tire树上走L步且不经过禁止节点的概率. 根据全概率公式用记忆化搜索求解. #include <cs ...
随机推荐
- Head First Python 代码和实例下载
http://python.itcarlow.ie/resources.html
- 《JavaScript DOM编程艺术》:+= 相加之后再赋值
第2章 第20页 += var year = 2010; var message = "The year is"; message += year; message += yea ...
- 【UE4 C++】抛物线路径、发射轨道相关
基于UGameplayStatics Blueprint_PredictProjectilePath_ByObjectType 根据 Object Type,算出抛物线的点集合和检测结果 static ...
- centos7 配置ftp服务器搭建(匿名访问,以及本地登录)
大家好,今天来给大家分享一个基于centos 7的ftp服务器搭建 实现功能:匿名访问,本地登录 查看系统版本: [root@localhost ~]# cat /etc/redhat-release ...
- UltraSoft - Beta - Scrum Meeting 7
Date: May 23rd, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 暂无 Liuzh 前端 编写忘记密码界面 Kkkk 前端 暂无 王fuji 前端 ...
- activiti会签 多实例例子
在实际的业务中,可能存在存在这么一种情况,当流程运行到某一个环节时,可能需要同时多个人的参与,才可以完成此环节.此时就可以用到activiti的多实例来解决此问题. 一.将一个节点设置成多实例的方法: ...
- android tcp通讯
Andoird TCP通讯 前言 最近在写一个即时通讯的项目,有一些心得,写出来给大家分享指正一下. 简单描述一下这个项目: 实时查询车辆运行状态的项目,走TCP通迅. 接口采用GZIP压缩. 后台是 ...
- camera isp(Image Signal Processor)
1. 目标[52RD.com] 手机摄像头模组用ISP功能模块的市场走向及研发方向.为能够正确认识手机摄像模组行业提供技术及市场依据.[52RD.com] 2. ISP在模组上的应用原理[52RD.c ...
- Envoy实现.NET架构的网关(二)基于控制平面的动态配置
什么是控制平面 上一篇我们讲了文件系统的动态配置,这次我们来看看通过Control Panel来配置Envoy.控制平面就是一个提供Envoy配置信息的单独服务,我们可以通过这个服务来修改Envoy的 ...
- 重学STM32---(十)之CAN通信(二)
目录 前言 程序编写 主代码 测试 前言 CAN协议是非常难的,但是在stm32中却是简单的,只需要我们配置寄存器即可,,,即使这样,我在学习的时候也遇到了许多困难 程序编写 1.开时钟,不用说 ...