YJC plays automaton
Accepts: 1
Submissions: 14
Time Limit: 12000/6000 MS (Java/Others)
Memory Limit: 524288/262144 K (Java/Others)
问题描述
YJC是个小火车老司机,所以他是袜子坊烧饼栏目举办的“吃的更圆”竞赛金牌获得者,他去吃特色菜时看到一个 n+1 个状态的自动机,编号为 0到n ,其中0号点表示 NULL ,他非常好奇于是开始了研究: 他选取了一个初始状态集合 S ,发现其有如下有趣性质: 存在一个字符串 str ,使得 S 中的每一个元素在自动机上运行了 str 之后,得到的结束状态集合包含 NULL 和至少一个 NULL 以外的状态,即 S 中运行 str 后到达 NULL 的状态数目在 [1,|S|−1] 中。 满足这个性质的集合叫做YJC集,注意YJC集不能包含 NULL 。 他想知道有多少个YJC集。 关于自动机在本题中你可以这样理解: 自动机有 n+1 个状态,编号 0 到 n ,其中 0 是 NULL 状态,设字符集大小为 m 。 有转移函数 δ(i,j)(0≤i≤n,1≤j≤m) ,满足 0≤δ(i,j)≤n且δ(0,j)=0 一个状态t在自动机上运行字符串str可以参考以下程序 for i=0..str.length−1 t←δ(t,str[i]) 你还可以查看 https://zh.wikipedia.org/wiki/自動機理論
输入描述
第一行两个正整数 n,m(1≤n≤888,1≤m≤8) ,表示自动机大小和字符集大小。 以下n行,每行m个元素,表示转移函数δ(i,j),即第i个状态读取到字符j的转移目标,若为0则表示转移到NULL。 NULL只能转移到NULL。 最终测试时,输入文件不超过11000行,共31组。 由于测试时是总时限,若你的程序最坏情况下可以在0.5s内通过1组极限数据(CPU 3.0 GHz),一般可以通过systemtest。
输出描述
一行一个整数,表示YJC集的个数。
答案对
998244353(7×17×223+1,一个质数)
取模。
输入样例
3 2 3 0 3 0 0 3
输出样例
3
Hint
可能的YJC集有: {1,3},{2,3},{1,2,3} 状态1,2是等价的,显然 1,2 不是YJC集
/*hdu 5278 YJC plays automaton
思路:
建立反向图,先考虑二元组,使用bfs把目的状态能到达的二元组记下来。
最后计算答案,好难表述。
*/
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define PII pair<int,int>
#define LL __int64
#define MP make_pair
#define PB push_back
#define X first
#define Y second
const int MOD=998244353;
const int N=1005;
const int M=10;
int tran[N][M];
LL two[N];
void predo(){
two[0]=1;
for(int i=1;i<N;++i)
two[i]=two[i-1]*2%MOD;
}
vector<PII> e[N][N];
PII que[N*N];
int front,tail;
int fa[N],num[N];
void init_bcj(){
for(int i=0;i<N;++i){
fa[i]=i;
num[i]=1;
}
}
int getFa(int x){
if(x!=fa[x]) return fa[x]=getFa(fa[x]);
return x;
}
void bin(int x,int y){
int fx=getFa(x);
int fy=getFa(y);
if(fx==fy) return ;
fa[fy]=fx;
num[fx]+=num[fy];
num[fy]=0;
}
int mark=0;
int used[N][N];
void gao(int n,int m){
++mark;
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
e[i][j].clear();
for(int x1=0;x1<=n;++x1){
for(int y1=x1+1;y1<=n;++y1){
for(int i=0;i<m;++i){
int x2=tran[x1][i];
int y2=tran[y1][i];
if(x2>y2) swap(x2,y2);
e[x2][y2].PB(MP(x1,y1));
}
}
}
front=tail=0;
for(int i=1;i<=n;++i){
que[tail++]=MP(0,i);
used[0][i]=mark;
}
while(front<tail){
PII now=que[front++];
for(int i=0;i<e[now.X][now.Y].size();++i){
int tmpx=e[now.X][now.Y][i].X;
int tmpy=e[now.X][now.Y][i].Y;
if(used[tmpx][tmpy]==mark) continue;
que[tail++]=MP(tmpx,tmpy);
used[tmpx][tmpy]=mark;
}
}
init_bcj();
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(used[i][j]!=mark) bin(i,j);
}
}
LL ans=(two[n]-1+MOD)%MOD;
for(int i=1;i<=n;++i){
if(num[i]){
//cout<<i<<' '<<num[i]<<endl;
ans=(ans-(two[num[i]]-1)+MOD)%MOD;
}
}
cout<<ans<<endl;
}
int main(){
predo();
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;++i)
for(int j=0;j<m;++j)
scanf("%d",&tran[i][j]);
gao(n,m);
}
return 0;
}