题目描述

  懒得写了。。。直接贴题面

  

  $\sum n\leq5000,1\leq S_{i,j}\leq k\leq 1000 $

题解

  先建出广义sam。

  可以发现朋友的出现位置的定义符合后缀自动机的right集合的定义,如果一群人会相互产生感情,那么这一群人的特征值序列一定是sam中的同一个点(right集合相同)。

  然后发现题目求的就是用最少的“从根开始,在根之外的点不想交的路径”覆盖整个sam。

  这是一个经典问题,可以用网络流解决。

  把除了根之外每个点拆成两个点,两个点之间连上下界都是\(1\)的边。sam中的转移连下界为\(0\)上界为\(1\)边。所有点都往汇点连下界为\(0\)上界为\(1\)的边。

  上下界最小流就是答案。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
#include<iostream>
#include<tr1/unordered_map>
#include<queue>
using namespace std;
using namespace tr1;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
namespace flow
{
int u[1000010];
int v[1000010];
int h[100010];
int t[1000010];
int c[1000010];
int cnt;
void add2(int a,int b,int d)
{
cnt++;
u[cnt]=a;
v[cnt]=b;
c[cnt]=d;
t[cnt]=h[a];
h[a]=cnt;
}
void add(int x,int y,int v)
{
add2(x,y,v);
add2(y,x,0);
}
int S,T;
void add(int x,int y,int u,int v)
{
add(x,T,u);
add(S,y,u);
add(x,y,v-u);
}
int num;
int e[1000010];
int d[1000010];
int cur[100010];
queue<int> q;
int op(int x)
{
return ((x-1)^1)+1;
}
void init()
{
memset(d,-1,sizeof d);
d[T]=0;
q.push(T);
while(!q.empty())
{
int x=q.front();
q.pop();
e[d[x]]++;
for(int i=h[x];i;i=t[i])
if(c[op(i)]&&d[v[i]]==-1)
{
d[v[i]]=d[x]+1;
q.push(v[i]);
}
}
}
int dfs(int x,int flow)
{
if(x==T)
return flow;
int s=0,u;
for(int &i=cur[x];i;i=t[i])
if(c[i]&&d[v[i]]==d[x]-1)
{
u=dfs(v[i],min(c[i],flow));
flow-=u;
s+=u;
c[i]-=u;
c[op(i)]+=u;
if(!flow)
return s;
}
e[d[x]]--;
if(!e[d[x]])
d[S]=num;
d[x]++;
e[d[x]]++;
cur[x]=h[x];
return s;
}
int solve()
{
int ans=0;
init();
memcpy(cur,h,sizeof h);
while(d[S]>=0&&d[S]<=num-1)
ans+=dfs(S,0x3fffffff);
return ans;
}
}
namespace sam
{
unordered_map<int,int> next[10010];
int fail[10010];
int len[10010];
int n;
void init()
{
n=1;
}
int insert(int p,int c)
{
if(next[p][c])
{
int np=next[p][c];
if(len[np]==len[p]+1)
return np;
int nq=++n;
len[nq]=len[p]+1;
next[nq]=next[np];
fail[nq]=fail[np];
fail[np]=nq;
for(;p&&next[p][c]==np;p=fail[p])
next[p][c]=nq;
return nq;
}
int np=++n;
len[np]=len[p]+1;
for(;p&&!next[p][c];p=fail[p])
next[p][c]=np;
if(!p)
fail[np]=1;
else
{
int q=next[p][c];
if(len[q]==len[p]+1)
fail[np]=q;
else
{
int nq=++n;
len[nq]=len[p]+1;
next[nq]=next[q];
fail[nq]=fail[q];
fail[q]=fail[np]=nq;
for(;p&&next[p][c]==q;p=fail[p])
next[p][c]=nq;
}
}
return np;
}
}
int main()
{
open("friend");
int k,m,now,n,x;
scanf("%d%d",&k,&m);
sam::init();
while(m--)
{
now=1;
scanf("%d",&n);
while(n--)
{
scanf("%d",&x);
now=sam::insert(now,x);
}
}
flow::S=2*sam::n+1;
flow::T=2*sam::n+2;
flow::num=2*sam::n+2;
for(int i=2;i<=sam::n;i++)
{
flow::add(i*2-2,i*2-1,1,1);
flow::add(i*2-1,2*sam::n,0,1);
}
for(int i=1;i<=sam::n;i++)
for(auto v:sam::next[i])
flow::add(2*i-1,2*v.second-2,0,1);
flow::solve();
flow::add(2*sam::n,1,0,0x3fffffff);
int ans=flow::solve();
for(int i=1;i<=flow::cnt;i+=6)
{
if(flow::c[i])
{
printf("0\n");
return 0;
}
if(flow::c[i+2])
{
printf("0\n");
return 0;
}
}
printf("%d\n",ans);
return 0;
}

【XSY2767】朋友 广义后缀自动机 网络流的更多相关文章

  1. bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 对[广义后缀自动机]的一些理解

    先说一下对后缀自动机的理解,主要是对构造过程的理解. 构造中,我们已经得到了前L个字符的后缀自动机,现在我们要得到L+1个字符的后缀自动机,什么需要改变呢? 首先,子串$[0,L+1)$对应的状态不存 ...

  2. BZOJ 3926 && ZJOI 2015 诸神眷顾的幻想乡 (广义后缀自动机)

    3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec Memory Limit: 512 MB Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽 ...

  3. BZOJ 3277 串 (广义后缀自动机)

    3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] De ...

  4. BZOJ 3473: 字符串 [广义后缀自动机]

    3473: 字符串 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 354  Solved: 160[Submit][Status][Discuss] ...

  5. BZOJ.2780.[SPOJ8093]Sevenk Love Oimaster(广义后缀自动机)

    题目链接 \(Description\) 给定n个模式串,多次询问一个串在多少个模式串中出现过.(字符集为26个小写字母) \(Solution\) 对每个询问串进行匹配最终会达到一个节点,我们需要得 ...

  6. BZOJ.3926.[ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

    题目链接 要对多个串同时建立SAM,有两种方法: 1.将所有串拼起来,中间用分隔符隔开,插入字符正常插入即可. 2.在这些串的Trie上建SAM.实际上并不需要建Trie,还是只需要正常插入(因为本来 ...

  7. 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并

    [CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...

  8. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  9. SP8093 JZPGYZ - Sevenk Love Oimaster(广义后缀自动机)

    题意 题目链接 Sol 广义后缀自动机板子题..和BZOJ串那个题很像 首先建出询问串的SAM,然后统计一下每个节点被多少个串包含 最后直接拿询问串上去跑就行了 #include<bits/st ...

随机推荐

  1. MySQL之索引原理

    --------------------------------------------------------------------------------堕落的状态,无疑是慢性自杀.想想自己为什 ...

  2. nginx Location 语法基础知识

    URL地址匹配是Nginx配置中最灵活的部分 Location 支持正则表达式匹配,也支持条件匹配,用户可以通过location指令实现Nginx对动丶静态网页的过滤处理. Nginx locatio ...

  3. Dockerfile centos7_tomcat7.0.64_jdk7u80

    FROM centos:7 MAINTAINER jiangzhehao WORKDIR /tmp RUN yum -y install net-tools ADD jdk-7u80-linux-x6 ...

  4. mariadb(第五章)视图、事物、索引、外键

    视图 对于复杂的查询,在多个地方被使用,如果需求发生了改变,需要更改sql语句,则需要在多个地方进行修改,维护起来非常麻烦 假如因为某种需求,需要将user拆房表usera和表userb,该两张表的结 ...

  5. 007-迅雷定时重启AutoHotkey脚本-20190411

    ;; 定时重启迅雷.ahk,;;~ 2019年04月11日;#SingleInstance,forceSetWorkingDir,%A_ScriptDir%DetectHiddenWindows,On ...

  6. Python入门-字符串常用方法

    Python 字符串 字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串. 创建字符串很简单,只要为变量分配一个值即可. var1 = 'Hello Worl ...

  7. C. Ayoub and Lost Array

    链接 [https://codeforces.com/contest/1105/problem/C] 题意 给你n,表示数组长度,元素的值是l到r,问有多少种方案使得所有元素和整除3 分析 思维dp, ...

  8. sql中distinct和order by问题的解决方案

    需求:根据PID字段对数据去重,根据Sort字段排序,需要显示这个两个字段. 如图,这是原始数据,先排序: 排序后发现两个项是重复的,需要去除一个, 因为Distinct对检查Select里面的每一列 ...

  9. linux下jenkins的时区设置问题

    https://blog.csdn.net/king_wang10086/article/details/76178711 [root@jenkins ~]# yum install -y ntpda ...

  10. Redis教程(Linux)

    这里汇总了从简单的安装到较为复杂的配置,由浅入深的学习redis... 一 , 安装 1) redis扩展安装 从官网上下载扩展压缩包 wget http://pecl.php.net/get/red ...