hdu 5278 YJC plays automaton

9 篇文章 0 订阅

YJC plays automaton

 
 Accepts: 1
 
 Submissions: 14
 Time Limit: 12000/6000 MS (Java/Others)
 
 Memory Limit: 524288/262144 K (Java/Others)
问题描述
YJC是个小火车老司机,所以他是袜子坊烧饼栏目举办的“吃的更圆”竞赛金牌获得者,他去吃特色菜时看到一个
   
   
    
    n+1
   
   个状态的自动机,编号为
   
   
    
    0n
   
   ,其中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)(0in,1jm)
   
   ,满足
   
   
    
    0δ(i,j)nδ(0,j)=0
   
   
一个状态t在自动机上运行字符串str可以参考以下程序

   
   
    
    for
   
     
   
   
    
    i=0..str.length1
   
   
    
   
   
    
    tδ(t,str[i])
   
   
你还可以查看
https://zh.wikipedia.org/wiki/自動機理論
输入描述
第一行两个正整数
   
   
    
    n,m(1n888,1m8)
   
   ,表示自动机大小和字符集大小。

   
   
    
    nmδ(i,j)ij0NULL
   
   

   
   
    
    NULLNULL
   
   

最终测试时,输入文件不超过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集
思路:建立反向图,先考虑二元组,使用bfs把目的状态能到达的二元组记下来。最后计算答案,好难表述。


/*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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值