• 【CF590E】Birthday(AC自动机+二分图匹配)


    点此看题面

    • 给定(n)个字符串,求最多能选出多少字符串使得它们两两不存在子串关系,并给出一组最优方案。
    • (nle750,sum|s_i|le10^7)

    (AC)自动机求偏序关系

    我们对所有字符串建出(AC)自动机,接着我们枚举每一个串,想要求出它的所有子串。

    众所周知子串是前缀的后缀,我们枚举这个串的每个前缀,则后缀就是(fail)树上从该节点一路到根的所有节点。

    但是,如果直接暴力跳(fail)树,复杂度(O(nsum|s_i|))显然爆炸。

    不过,由于这道题我们要求所有偏序关系,实际上对于每个前缀只需求出最长一个符合条件的后缀,最后再统一跑一遍(Floyd)即可。

    而要求出最长后缀,就是(fail)树上最近的一个终止节点。

    本来我们建(AC)自动机的时候就需要(BFS),直接借助(BFS)队列(或直接在(BFS)的同时),令每个点从父节点转移即可。

    (Dilworth)定理+二分图匹配

    现在的题面就和【洛谷4298】[CTSC2008] 祭祀完全一样了。

    利用(Dilworth)定理转化题意,然后上二分图匹配即可。

    代码:(O(sum|s_i|+n^4))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 750
    #define SZ 10000000
    using namespace std;
    int n,m,bg[N+5],l[N+5],f[N+5][N+5];char s[SZ+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	I void reads(int& l,char* s) {l=0;W(isspace(oc=tc()));W(s[++l]=oc,!isspace(oc=tc())&&~oc);}
    }using namespace FastIO;
    namespace AC//AC自动机求偏序关系
    {
    	int Nt=1;struct node {int P,F,S[2];}O[SZ+5];
    	I void Ins(CI id,CI l,char* s)//插入字符串
    	{
    		RI x=1;for(RI i=1,t;i<=l;++i) !O[x].S[t=s[i]&1]&&(O[x].S[t]=++Nt),x=O[x].S[t];O[x].P=id;
    	}
    	int q[SZ+5];I void Build()//建AC自动机
    	{
    		RI i,k,H=1,T=0;for(i=0;i<=1;++i) (O[1].S[i]?O[q[++T]=O[1].S[i]].F:O[1].S[i])=1;
    		W(H<=T) for(k=q[H++],i=0;i<=1;++i) (O[k].S[i]?O[q[++T]=O[k].S[i]].F:O[k].S[i])=O[O[k].F].S[i];
    		for(i=1;i<=Nt;++i) k=q[i],!O[k].P&&(O[k].P=O[O[k].F].P);//记录每个点向上第一个终止节点
    	}
    	I void Work(CI id,CI l,char* s)//求每个前缀的最长后缀
    	{
    		for(RI i=1,x=1,t;i<=l;++i) x=O[x].S[s[i]&1],(t=i^l?O[x].P:O[O[x].F].P)&&(f[t][id]=1);//最后一个位置特判要从父节点取
    	}
    }
    namespace H//匈牙利算法
    {
    	int p[N+5],s[N+5],vis[N+5];I bool Match(CI x,CI ti)//二分图匹配
    	{
    		for(RI i=1;i<=n;++i) if(f[x][i]&&
    			!p[i]&&vis[i]^ti&&(vis[i]=ti,!s[i]||Match(s[i],ti))) return s[i]=x;
    		return 0;
    	}
    	I int Calc()
    	{
    		RI i,t=0;for(i=1;i<=n;++i) s[i]=vis[i]=0;for(i=1;i<=n;++i) !p[i]&&(t+=!Match(i,i));return t;//总点数-最大二分图匹配
    	}
    }
    int main()
    {
    	RI i,j,k;for(read(n),i=1;i<=n;++i) bg[i]=bg[i-1]+l[i-1],reads(l[i],s+bg[i]),AC::Ins(i,l[i],s+bg[i]);
    	for(AC::Build(),i=1;i<=n;++i) AC::Work(i,l[i],s+bg[i]);//利用AC自动机初步求出偏序关系
    	for(k=1;k<=n;++k) for(i=1;i<=n;++i) for(j=1;j<=n;++j) f[i][j]|=f[i][k]&f[k][j];//Floyd求出所有偏序关系
    	RI t=k=H::Calc();for(printf("%d
    ",k),i=1;i<=n;++i) if(!H::p[i])//已删除节点不再考虑
    	{
    		for(j=1;j<=n;++j) H::p[j]+=i==j||f[j][i]||f[i][j];if(H::Calc()==t-1) {printf("%d ",i),--t;continue;}//判断能否在最优构造方案中
    		for(j=1;j<=n;++j) H::p[j]-=i==j||f[j][i]||f[i][j];//不能则撤销影响
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    MySql msi安装
    C# TextBox文本内容选中
    SQL 删除时间最靠前的几条数据
    Layui表格工具栏绑定事件失效问题
    Layui我提交表单时,table.reload(),表格会请求2次,是为什么?按下面的做
    table 中数据行循环滚动
    html 3D反转效果
    网页电子表数字样式
    power tool 强制撤销
    GHOST -ntexact 正常还原
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF590E.html
Copyright © 2020-2023  润新知