bzoj 1004(置换+逆元)

1 篇文章 0 订阅


1004: [HNOI2008]Cards

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 2897   Solved: 1733
[ Submit][ Status][ Discuss]

Description

  小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有
多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方
案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗
成另一种.Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

Input

  第一行输入 5 个整数:Sr,Sb,Sg,m,p(m<=60,m+1<p<100)。n=Sr+Sb+Sg。
接下来 m 行,每行描述一种洗牌法,每行有 n 个用空格隔开的整数 X1X2...Xn,恰为 1 到 n 的一个排列,
表示使用这种洗牌法,第 i位变为原来的 Xi位的牌。输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代
替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。

Output

  不同染法除以P的余数

Sample Input

1 1 1 2 7
2 3 1
3 1 2

Sample Output

2

HINT

  有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG

和GRB。

100%数据满足 Max{Sr,Sb,Sg}<=20。


解题思路:置换要包括不动的,然后在求出等价的情况,取个平均数,因为要模

加个逆元。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std ;
inline int read ( )
{
     int x = 0 ; char ch = getchar ( ) ;
     while ( ch < '0' || ch > '9' ) ch = getchar ( ) ;
     while ( ch >= '0' && ch <= '9' ) { x = x * 10 + ch - '0' ; ch = getchar ( ) ; }
     return x ;
}
int s1 , s2 , s3 , n , m , mod , ans ;
int a [ 70 ] [ 70 ] , f [ 70 ] [ 70 ] [ 70 ] , d [ 70 ] ;
bool b [ 70 ] ;
int dp ( int x )
{
     for ( int i = 1 ; i <= n ; i ++ ) b [ i ] = 0 ;
     int sum = 0 , p ;
     for ( int i = 1 ; i <= n ; i ++ )
       if ( ! b [ i ] )
       {
           d [ ++ sum ] = 1 ; p = i ;
           b [ p ] = 1 ;
           while ( ! b [ a [ x ] [ p ] ] )
           {
               d [ sum ] ++ ;
               b [ a [ x ] [ p ] ] = 1 ;
               p = a [ x ] [ p ] ;
           }
       }
     for ( int i = s1 ; i >= 0 ; i -- )
         for ( int j = s2 ; j >= 0 ; j -- )
             for ( int k = s3 ; k >= 0 ; k -- )
                 f [ i ] [ j ] [ k ] = 0 ;
     f [ 0 ] [ 0 ] [ 0 ] = 1 ;
     for ( int h = 1 ; h <= sum ; h ++ )
         for ( int i = s1 ; i >= 0 ; i -- )
             for ( int j = s2 ; j >= 0 ; j -- )
                 for ( int k = s3 ; k >= 0 ; k -- )
                 {
                     if ( i >= d [ h ] ) f [ i ] [ j ] [ k ] = ( f [ i ] [ j ] [ k ] + f [ i - d [ h ] ] [ j ] [ k ] ) % mod ;
                     if ( j >= d [ h ] ) f [ i ] [ j ] [ k ] = ( f [ i ] [ j ] [ k ] + f [ i ] [ j - d [ h ] ] [ k ] ) % mod ;
                     if ( k >= d [ h ] ) f [ i ] [ j ] [ k ] = ( f [ i ] [ j ] [ k ] + f [ i ] [ j ] [ k - d [ h ] ] ) % mod ;
                 }
     return f [ s1 ] [ s2 ] [ s3 ] ;
}
void exgcd ( int a , int b , int &x , int &y )
{
     if ( b == 0 ) { x = 1 ; y = 0 ; return ; }
     exgcd ( b , a % b , x , y ) ;
     int t = x ; x = y ; y = t - a / b * y ;
}
int main ( )
{
     s1 = read ( ) , s2 = read ( ) , s3 = read ( ) , m = read ( ) , mod = read ( ) ;
     n = s1 + s2 + s3 ;
     for ( int i = 1 ; i <= m ; i ++ )
         for ( int j = 1 ; j <= n ; j ++ )
             a [ i ] [ j ] = read ( ) ;
     m ++ ;
     for ( int i = 1 ; i <= n ; i ++ ) a [ m ] [ i ] = i ;
     for ( int i = 1 ; i <= m ; i ++ )
         ans = ( ans + dp ( i ) ) % mod ;
     int x , y ;
     exgcd ( m , mod , x , y ) ;
     while ( x <= 0 ) x += mod , y -= m ;
     printf ( "%d" , ans * x % mod ) ;
     return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值