当前位置:网站首页>P6698-[BalticOI 2020 Day2]病毒【AC自动机,dp,SPFA】
P6698-[BalticOI 2020 Day2]病毒【AC自动机,dp,SPFA】
2022-06-24 07:49:00 【QuantAsk】
正题
题目链接:https://www.luogu.com.cn/problem/P6698
题目大意
有一个包含 0 ∼ G − 1 0\sim G-1 0∼G−1的字符集,其中有 n n n种变换,能够将一个字符 a i ( a i > 1 ) a_i(a_i>1) ai(ai>1)变为一串字符 b i b_i bi,当一个字符串中只剩下 0 0 0和 1 1 1时变换就结束了。
然后给出 m m m个匹配串 c i c_i ci。现在对于每个字符 i ∈ [ 2 , G − 1 ] i\in[2,G-1] i∈[2,G−1],是否字符 i i i无论变化结束时都含有至少一个匹配串,如果不是,求一个最短的不包含任何匹配串的最终串长度。
保证每个在 [ 2 , G − 1 ] [2,G-1] [2,G−1]的字符都能进行变化,匹配串中只包含 0 / 1 0/1 0/1。
∑ t i ≤ 50 , ∑ ∣ b i ∣ ≤ 100 , n ≤ 100 , 2 < G ≤ n + 2 \sum t_i\leq 50,\sum |b_i|\leq 100,n\leq 100,2<G\leq n+2 ∑ti≤50,∑∣bi∣≤100,n≤100,2<G≤n+2
解题思路
有匹配的问题,我们先拿所有的 c i c_i ci出来建一棵AC自动机,然后考虑怎么计算每个字符能从哪个状态跳到哪个状态。
注意我们在AC自动机上走状态时不能走到包含匹配串的状态,这样我们可以将无论如何都包含匹配串视为最短不包含匹配串的长度无穷大。
那么我们考虑dp这个最短的长度,设 f i , s , t f_{i,s,t} fi,s,t表示字符 i i i经过变换后,能从状态 s s s走到状态 t t t的最短长度。
转移时我们考虑枚举一个变换 i i i,再枚举一个起点 s s s,然后设 g j , x g_{j,x} gj,x表示现在走到 b i , j b_{i,j} bi,j,在状态 x x x时的最短长度,那么转移时就有
g j + 1 , y = m i n { g j , x + f b i , j , x , y } g_{j+1,y}=min\{g_{j,x}+f_{b_{i,j},x,y}\} gj+1,y=min{ gj,x+fbi,j,x,y}
但是会注意到 f i , s , t f_{i,s,t} fi,s,t之间的转移并不是一个单向的关系,会发现这是一个和最短路很类似的转移,我们考虑魔改一下SPFA。
我们先把 0 , 1 0,1 0,1加入队列,然后每次取出队头 x x x,我们把所有包含字符 x x x的 b i b_i bi都拿出来跑一次 g g g,如果这次跑出来的 g g g能够更新 f a i , s , t f_{a_i,s,t} fai,s,t,那么我们就把 a i a_i ai入队(如果之前 a i a_i ai不在队列中)。
我们视SPFA的复杂度为 O ( n 2 ) O(n^2) O(n2),记AC自动机状态数为 k k k,每做一次转移应该是 O ( ∣ b i ∣ k 3 ) O(|b_i|k^3) O(∣bi∣k3),那么这题中SPFA的复杂度应该就是 O ( G ∑ ∣ b i ∣ k 3 ) O(G\sum |b_i|k^3) O(G∑∣bi∣k3),实际上跑起来常数会很小,可以通过本题。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#define ll long long
using namespace std;
const ll N=105,inf=1e18;
ll G,n,m,a[N],f[N][N][N],g[N][52];
ll cnt,ch[N][2],fail[N];bool ed[N],v[N];
queue<int> q;vector<int> b[N],T[N];
void ins(ll n){
ll x=0;
for(ll i=1,c;i<=n;i++){
scanf("%lld",&c);
if(!ch[x][c])ch[x][c]=++cnt;
x=ch[x][c];
}
ed[x]=1;return;
}
void getfail(){
for(ll i=0;i<2;i++)
if(ch[0][i])q.push(ch[0][i]);
while(!q.empty()){
ll x=q.front();q.pop();
ed[x]|=ed[fail[x]];
for(ll i=0;i<2;i++){
if(ch[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];
q.push(ch[x][i]);
}
else ch[x][i]=ch[fail[x]][i];
}
}
return;
}
bool solve(int i){
bool flag=0;
for(ll s=0;s<=cnt;s++){
if(ed[s])continue;
memset(g,0x3f,sizeof(g));g[0][s]=0;
for(ll j=0;j<b[i].size();j++){
for(ll x=0;x<=cnt;x++){
if(g[j][x]>=inf)continue;
for(ll y=0;y<=cnt;y++)
g[j+1][y]=min(g[j+1][y],g[j][x]+f[b[i][j]][x][y]);
}
}
for(ll x=0;x<=cnt;x++){
flag|=(g[b[i].size()][x]<f[a[i]][s][x]);
f[a[i]][s][x]=min(f[a[i]][s][x],g[b[i].size()][x]);
}
}
return flag;
}
void SPFA(){
q.push(0);q.push(1);v[0]=v[1]=1;
while(!q.empty()){
int x=q.front();q.pop();v[x]=0;
for(int i=0;i<T[x].size();i++){
int y=T[x][i];
if(solve(y)&&!v[a[y]])
q.push(a[y]),v[a[y]]=1;
}
}
return;
}
signed main()
{
scanf("%lld%lld%lld",&G,&n,&m);
for(ll i=1,k;i<=n;i++){
scanf("%lld%lld",&a[i],&k);
for(ll j=1,x;j<=k;j++){
scanf("%lld",&x),b[i].push_back(x);
T[x].push_back(i);
}
}
for(ll i=1,k;i<=m;i++){
scanf("%lld",&k);
ins(k);
}
getfail();ll k=0;
memset(f,0x3f,sizeof(f));
for(ll i=0;i<=cnt;i++){
if(ed[i])continue;
if(!ed[ch[i][0]])f[0][i][ch[i][0]]=1;
if(!ed[ch[i][1]])f[1][i][ch[i][1]]=1;
}
SPFA();
for(ll i=2;i<G;i++){
ll ans=inf;
for(ll x=0;x<=cnt;x++)
ans=min(ans,f[i][0][x]);
if(ans>=inf)puts("YES");
else printf("NO %lld\n",ans);
}
return 0;
}
边栏推荐
- Groovy通过withCredentials获取Jenkins凭据
- 解决:jmeter5.5在win11下界面上的字特别小
- [redis realize Secondary killing Business ①] Overview of Secondary killing Process | Basic Business Realization
- 110. balanced binary tree recursive method
- Become an IEEE student member
- Yolox backbone -- implementation of cspparknet
- Implementation process of tcpdump packet capturing
- Data middle office: the data middle office practice scheme of Minsheng Bank
- [noi Simulation Competition] send (tree DP)
- [Niuke] length of the last word of HJ1 string
猜你喜欢

【LeetCode】415. String addition

Webrtc series - network transmission 5: select the optimal connection switching

Numpy numpy中的np.c_和np.r_详解

leetcode——错误的集合

2022-06-23:给定一个非负数组,任意选择数字,使累加和最大且为7的倍数,返回最大累加和。 n比较大,10的5次方。 来自美团。3.26笔试。

【gdb调试工具】| 如何在多线程、多进程以及正在运行的程序下调试
![[ES6 breakthrough] promise is comparable to native custom encapsulation (10000 words)](/img/b3/b156d75c7b4f03580c449f8499cd74.png)
[ES6 breakthrough] promise is comparable to native custom encapsulation (10000 words)

数云发布2022美妆行业全域消费者数字化经营白皮书:全域增长破解营销难题

Remote connection of raspberry pie without display by VNC viewer

浮点数表示法(总结自CS61C和CMU CSAPP)
随机推荐
【Redis實現秒殺業務①】秒殺流程概述|基本業務實現
The printed object is [object object]. Solution
"I can't understand Sudoku, so I choose to play Sudoku."
[noi Simulation Competition] geiguo and time chicken (structure)
Huawei Router: GRE Technology
Data middle office: middle office architecture and overview
【LeetCode】415. 字符串相加
Huawei Router: IPSec Technology
【牛客】把字符串转换成整数
解决:jmeter5.5在win11下界面上的字特别小
uniapp 开发多端项目如何配置环境变量以及区分环境打包
【LeetCode】387. 字符串中的第一个唯一字符
阿里资深软件测试工程师推荐测试人员必学——安全测试入门介绍
[redis realize Secondary killing Business ①] Overview of Secondary killing Process | Basic Business Realization
Linux MySQL installation
Kaformer personal notes
Depens:*** but it is not going to be installed
2022-06-23:给定一个非负数组,任意选择数字,使累加和最大且为7的倍数,返回最大累加和。 n比较大,10的5次方。 来自美团。3.26笔试。
tcpdump抓包实现过程
随笔-反思