Codeforces Beta Round #83 (Div. 1 Only)

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove   

在HFUT周赛的时候做的。

A:水题嘛,多条链,枚举链的头部,也就是入度为0,出度为1的,然后遍历链求最小值。

B:水题嘛,给出m组人,每一组有ai个人,其中本人在第h组,现在在组一个队有n人(肯定包括本人),至少有一个人和自己是在同一组的概率。

也就是要取n-1个人,第h组中还有a个人,那么剩下的m-1其实本质一样,假设有b个人。

题意就是说从a+b个中取n-1个人,至少有一个人是a个人中的概率为多少。

考虑没有一个人和本人一组,便是C(b,n-1)/C(a+b,n-1) ,那么结果就是1-C(b,n-1)/C(a+b,n-1)

开始姿势不够优越,遇到了点精度问题,注意小优化一下再搞。

C:n个位置,有m种关系,表示坐在ai上的人的标号要比坐在bi上的人的标号小。给出满足限制的第y-2000个字典序排列的安排。(艹,你这也算翻译题意。。。)

由于是按字典序排的,那么我们从高位开始枚举,逐位确定。例如,我们要求第k个字典序安排,第一步假设第一个位置做的是1号人物。那么判断在这样的条件下,有c个满足条件的安排。如果c<k那么继续往后枚举,

如果c>k表示当前位就是1号人物,则枚举下一位。

至于对于一定的限制怎么求出有多少个满足条件的安排,显然是状态压缩DP,总共16个人,比较敏感的数字。

具体不说了,显然DP不是我的菜,我是渣渣

D:不错的题目,给出c组限制,ci,ti,表示当串中的字母ci的个数为ti的倍数的时候,该类型不会被惩罚。

一个字母可能有多个限制,至少需要满足其中一个限制就够了,每个字母都要满足。

问长度 为n的串有多少个满足条件的。

被数据范围吓尿了,当时还在想,MOD不是很大,是不是组合数用卢卡斯定理呢。

但是26个类型,n的范围是1e18,即使是n是100也做不来。。。果然放弃。

正解也很巧妙。注意题目说了所有的限制的乘积<=123。

则想到一种dp,dp[i][j]表示长度为i,状态为j的个数。那么这个状态怎么表示呢。

对于每一个限制,我们记录当前字母的个数mod 限制 ti 的值。

显然有sigma(ti)种情况,如果当前表示某种限制的数字为0,则说明mod ti==0,说明是ti的倍数,也就是满足条件。

(艹,这也算题解???自己都看不懂)

还是举个例子吧

如果

A 2

B 3

C 2

那么我们用0-11表示所有状态

状态    A的个数       B的个数              C的个数

0          0%2 =0     (0/2)%3=0        (0/2/3)%2=0;

1          1%2 =1     (1/2)%3=0        (1/2/3)%2=0;

2          2%2 =0     (2/2)%3=1        (2/2/3)%2=0;

3          3%2 =1     (3/2)%3=1        (3/2/3)%2=0;

4          4%2 =0     (4/2)%3=2        (4/2/3)%2=0;

5          5%2 =1     (5/2)%3=2        (5/2/3)%2=0;

6          6%2 =0     (6/2)%3=0        (6/2/3)%2=1;

7          7%2 =1     (7/2)%3=0        (7/2/3)%2=1;

8          8%2 =0     (8/2)%3=1        (8/2/3)%2=1;

9          9%2 =1     (9/2)%3=1        (9/2/3)%2=1;

10          10%2 =0    (10/2)%3=2        (10/2/3)%2=1;

11          11%2 =1    (11/2)%3=2        (11/2/3)%2=1;

然后对于某一个状态,像以下这样拆开,进行状态转移,得到新的状态,用矩阵记录一下,n很大,只 能矩阵快速幂

感谢 zz1215的指导

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<queue>
#define inf 100000005
#define M 200005
#define N 125
#define maxn 300005
#define eps 1e-10
#define zero(a) fabs(a)<eps
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define MOD 12345
#define lson step<<1
#define rson step<<1|1
#define sqr(a) ((a)*(a))
#define Key_value ch[ch[root][1]][0]
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
struct Matrix{
    int m[N][N];
    Matrix(){mem(m,0);}
}Init,Ret;
Matrix mul(Matrix a,Matrix b,int n){
    Matrix ans;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            for(int k=0;k<n;k++)
                ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j])%MOD;
    return ans;
}
Matrix PowMod(Matrix a,LL m,int n){
    Matrix ans;
    for(int i=0;i<n;i++) ans.m[i][i]=1;
    while(m){
        if(m&1) ans=mul(ans,a,n);
        a=mul(a,a,n);
        m/=2;
    }
    return ans;
}
void Debug(Matrix a,int n){
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++) printf("%d ",a.m[i][j]);
        printf("\n");
    }
}
LL n;
int c,flag[26],cnt,val[1005],id[1005];
void Bulid(){
    mem(Init.m,0);
    //枚举状态
    for(int i=0;i<cnt;i++){
        int mod[c],tmp=i;
        //分解
        for(int j=0;j<c;j++){
            mod[j]=tmp%val[j];
            tmp/=val[j];
        }
        for(int j=0;j<26;j++){
            if(flag[j]){
                //状态转移,相应的字母+1
                for(int k=0;k<c;k++){
                    if(id[k]==j){
                        mod[k]++;
                        if(mod[k]>=val[k]) mod[k]-=val[k];
                    }
                }
                int now=0;
                //得到新的状态
                for(int k=c-1;k>=0;k--) now=now*val[k]+mod[k];
                Init.m[i][now]++;
                //恢复现场
                for(int k=0;k<c;k++){
                    if(id[k]==j){
                        mod[k]--;
                        if(mod[k]<0) mod[k]+=val[k];
                    }
                }
            }
        }
    }
    for(int i=0;i<cnt;i++) for(int j=0;j<cnt;j++) Init.m[i][j]%=MOD;
}
//判断状态k满足条件,每种字母的条件至少满足一个
bool check(int k){
    bool ok[26];mem(ok,false);
    for(int i=0;i<c;i++){
        int tmp=k%val[i];
        k/=val[i];
        if(tmp==0) ok[id[i]]=true;
    }
    for(int i=0;i<26;i++) if(flag[i]&&!ok[i]) return false;
    return true;
}
char str[10];
int main(){
    while(scanf("%I64d%d",&n,&c)!=EOF){
        cnt=1;mem(flag,0);
        for(int i=0;i<c;i++){
            scanf("%s%d",str,&val[i]);
            id[i]=str[0]-'A';
            cnt*=val[i];
            flag[id[i]]=1;
        }
        Bulid();
        //Debug(Init,cnt);
        Ret=PowMod(Init,n,cnt);
        //Debug(Ret,cnt);
        int ans=0;
        for(int i=0;i<cnt;i++){
            if(check(i)) ans+=Ret.m[0][i];
        }
        printf("%d\n",ans%MOD);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值