首页 > 其他分享 >题解:CF590E Birthday

题解:CF590E Birthday

时间:2024-08-25 21:08:04浏览次数:10  
标签:AC idx int 题解 CF590E arr Birthday && sum

题目分析

题意

给定 \(n\) 个字符串,要求从中选出若干个组成一个集合,且集合中每个字符串都互不包含。

求集合最大包含几个字符串。

分析

本题弱化版:[ABC354G] Select Strings

就是求一个最长反链,并求构造方案。

求构造方案还是比较有意思的。

建议先做 P4298 [CTSC2008] 祭祀


一开始用的 KMP 暴力匹配字符串。

然后:

\(n \le 750\),\(\sum_{i=1}^n \limits |s_i| \le 10^7\)。

KMP 直接 TLE on #64。


KMP 挂了,考虑上 AC 自动机。

建好 fail 指针后考虑从串的末尾节点跳 fail,每跳一次判断到根的路径上有无结尾标记,进而判断包含关系。

然而一次判断就是 \(O(\sum|s_i|)\),同样 TLE。

我们可以标记跳 fail 的路径,然后模拟插入串的过程。

如果遇到有标记的节点,那么就向标记它的节点连边。

因为我们最后会用 floyd 传递闭包,所以标记跳 fail 的路径直接覆盖标记即可。

这样就能 \(O(\sum|s_i|)\) 完成建图。

总时间复杂度 \(O(\sum|s_i| + n^3)\)。

Code

#include<bits/stdc++.h>
using namespace std;

template<typename Tp, size_t sizn, size_t sizm>
struct netflow
{
    int cnt=1, s=sizn-2, t=sizn-3;
    Tp val[sizm<<1], dis[sizn];

    void link(int u, int v, Tp w)
    {
        // println(cerr, "{} {} {}", u, v, w);
        to[++cnt]=v;       val[cnt]=w;
        nxt[cnt]=head[u]; head[u]=cnt;
        to[++cnt]=u;       val[cnt]=0;
        nxt[cnt]=head[v]; head[v]=cnt;
    }

    int head[sizn], to[sizm<<1], nxt[sizm<<1], now[sizm<<1];
    Tp inf=numeric_limits<Tp>::max()/2;
    int bfs()
    {
        for(int i=1;i<sizn;i++) dis[i]=inf;
        dis[t]=inf;
        queue<int> q;
        q.push(s);
        dis[s]=0;
        now[s]=head[s];
        while(!q.empty())
        {
            int idx=q.front(); q.pop();
            for(int i=head[idx];i;i=nxt[i])
            {
                int arr=to[i];
                if(val[i]>0&&dis[arr]==inf)
                {
                    q.push(arr);
                    now[arr]=head[arr];
                    dis[arr]=dis[idx]+1;
                    if(arr==t) return 1;
                }
            }
        }
        return 0;
    }

    Tp dfs(int idx, Tp sum)
    {
        if(idx==t) return sum;
        Tp k, res=0;
        for(int i=now[idx];i&&sum;i=nxt[i])
        {
            now[idx]=i;
            int arr=to[i];
            if(val[i]>0&&(dis[arr]==dis[idx]+1))
            {
                k=dfs(arr, min(sum, val[i]));
                if(k==0) dis[arr]=inf;
                val[i]-=k;      res+=k;
                val[i^1]+=k;    sum-=k;
            }
        }
        return res;
    }

    Tp maxflow()
    {
        Tp ans=0;
        while(bfs()) ans+=dfs(s, inf);
        return ans;
    }
};

netflow<int, 100005, 1000005> nf;
#define maxn 10000007
#define maxm 802

#define l(x) ((x)<<1)
#define r(x) ((x)<<1|1)

namespace AC
{
    int ch[maxn][2], fail[maxn], flg[maxn];
    int siz=0;

    void insert(string m, int idx)
    {
        int now=0;
        for(auto i:m)
        {
            if(!ch[now][i-'a'])
                ch[now][i-'a']=++siz;
            now=ch[now][i-'a'];
        }
        flg[now]=idx;
    }

    void build_fail()
    {
        queue<int> q;
        for(int i=0;i<2;i++)
            if(ch[0][i]) 
                q.push(ch[0][i]);
        while (!q.empty())
        {
            int now=q.front();
            q.pop();
            for(int i=0;i<2;i++)
                if(ch[now][i])
                {
                    int v=ch[now][i];
                    fail[v]=ch[fail[now]][i];
                    if(!flg[v]) 
                        flg[v]=flg[fail[v]];
                    q.push(v);
                }
                else ch[now][i]=ch[fail[now]][i];
        }
    }

};

string ss[maxm];
int tag[maxm<<1];
bitset<maxm> bs[maxm];

void dfs(int u)
{
    tag[u]=1;
    for(int i=nf.head[u];i;i=nf.nxt[i])
    {
        int v=nf.to[i];
        if(v!=nf.t&&v!=nf.s&&nf.val[i]&&!tag[v])
            dfs(v);
    }
}

void search(int i)
{
    int u=0;
    for(auto c:ss[i])
    {
        if(AC::flg[u]&&AC::flg[u]!=i) 
            bs[i][AC::flg[u]]=1;
        u=AC::ch[u][c-'a'];
    }
    if(AC::flg[AC::fail[u]]&&AC::flg[AC::fail[u]]!=i) 
        bs[i][AC::flg[AC::fail[u]]]=1;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>ss[i];
        AC::insert(ss[i], i);
    }
    AC::build_fail();
    for(int i=1;i<=n;i++) search(i);
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
            if(bs[i][j])
                bs[i]|=bs[j];
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
            if(bs[i][j]) nf.link(l(i), r(j), nf.inf);
    
    for(int i=1;i<=n;i++)
    {
        nf.link(nf.s, l(i), 1);
        nf.link(r(i), nf.t, 1);
    }
    int ret=nf.maxflow();
    cout<<(n-ret)<<'\n';
    for(int i=nf.head[nf.s];i;i=nf.nxt[i])
        if(nf.val[i]) dfs(nf.to[i]);
    for(int i=1;i<=n;i++) 
        if(tag[l(i)]&&!tag[r(i)]) cout<<i<<' ';
}

标签:AC,idx,int,题解,CF590E,arr,Birthday,&&,sum
From: https://www.cnblogs.com/redacted-area/p/18379549

相关文章

  • 题解:P5217 贫穷
    题意维护一个字符串,支持以下操作:\(\texttt{Ixc}\),在第\(x\)个字母后面插入一个\(c\)。\(\texttt{Dx}\),删除第\(x\)个字母。\(\texttt{Rxy}\),反转当前文本中的区间\([x,y]\)。\(\texttt{Px}\),输出初始文本中第\(x\)个字母在当前文本中的位置。特别地,若不存在,......
  • 题解:UVA11996 Jewel Magic
    题意给你一个01串,要求完成以下操作:单点插入。单点删除。区间翻转。查询两点开始的LCP。分析先看查询操作,如何得到LCP的长度?我们可以考虑二分长度\(l\),然后用哈希检验区间\([p1,p1+l-1]\)是否等于区间\([p2,p2+l-1]\)。平衡树维护哈希即可。发现......
  • 题解:AT_abc354_g [ABC354G] Select Strings
    题目分析题意给定\(n\)个字符串,要求从中选出若干个组成一个集合,且集合中每个字符串都互不包含。求集合中字符串的权值的和的最大值。分析首先很容易想到用KMP判两个串是否存在包含关系。考虑建图,将不能同时存在于一个集合中的串的节点相连。然后发现只需求出这个图的最......
  • 题解:P7952 [✗✓OI R1] 天动万象
    提供一种和第一篇题解不同的理解思路。题目分析看到操作\(1\):拿dfs序水水就行了。看到操作\(2\):???特殊情况我们考虑一下特殊情况下操作\(2\)怎么处理。假如这棵树是一条链。设从根到叶节点权值如下:(随便赋的)节点编号123456权值123456如果我们......
  • 题解:CF1995C Squaring
    题意给定序列\(a\),每次操作可以使\(a_i\getsa_i^2\),求使\(a\)不降的最少操作次数。分析因为\(1^k=1\),所以无解的情况只有\(\exist\a_i=1\)且\(\exist\j\in[1,i),a_j>1\)。在有解的情况下,假设对\(a_{i-1}\)操作\({k_{i-1}}\)次,对\(a_i\)操作\({k_i}\)次。......
  • 题解:UVA1479 Graph and Queries
    分析先看删边操作。由于并不保证是森林,所以我们没有好的方法来在线维护删边相关的操作。所以,我们可以套路地把询问离线,然后倒着操作。删边变成加边。需要注意的是权值的修改,记录时要把当前权值和修改的权值反过来。然后我们发现这个操作很经典,维护方式和[HNOI2012]永无乡......
  • 题解:P7475 「C.E.L.U-02」简易输入法
    题意给定词典\(\text{U}\),每次询问读入一个字符串\(s\),以及一个整数\(x\)对于这个字符串有以下几种情形:设\(s_i\in\text{U}\)且\(s\)为\(s_i\)的前缀的个数为\(a\)。当\(a\gex\)时,请输出按照以输出次数从大到小为第一关键字,以字典序为第二关键字排序后的第\(x......
  • 题解:P7401 [COCI2020-2021#5] Planine
    题意现有一座上下起伏的山。它可以抽象为一个包含\(n\)(\(n\)为奇数)个点\((x_i,y_i)\)以及\((x_1,-\inf)\)与\((x_n,-\inf)\)的多边形。对于所有满足\(i\neq1\),\(i\neqn\),\(i\bmod2=1\)的整数\(i\),\((x_i,y_i)\)都是山谷。现要放置若干个高度为\(h\)的点光......
  • 题解:SP1182 SORTBIT - Sorted bit squence
    题意将区间\([m,n]\)的所有整数按照其二进制位表示的数中\(1\)的数量从小到大排序。如果\(1\)的数量相同,则按照数的大小排序。求序列中第\(k\)个数。其中,负数使用补码来表示:一个负数的二进制数与其相反数的二进制数之和恰好等于\(2^{32}\)。分析考虑用uint32_t存......
  • 题解:P3266 [JLOI2015] 骗我呢
    题意有一个\(n\timesm\)的数组\(x_{i,j}(1\lei\len,1\lej\lem)\),满足:\(x_{i,j}\in[0,m]\)\(\foralli\in[1,n],\forallj\in[1,m),x_{i,j}<x_{i,j+1}\)\(\foralli\in(1,n],\forallj\in[1,m),x_{i,j}<x_{i-1,j+1}\)......