COCI. PALACINKE


 PALACINKE

Ana 举办一个在家同学聚会,当同学们还有 T 分钟就要到时,他发现自己还少准备了四样东西。(flour, milk, eggs and jam). 于是他驾车到附近准备购买这些东西。附近有 N 个地点 被标记成 1 to N, 以及 M 条单向道路。 Ana 从地点1出发。路上他可以直接到下一地点(要1分钟),或购买了东西后到下一地点(要2分钟)当然路上有可能买不完所有的东西。当然他可以在到下一地点的路上买。以下是一个关于 5个点7条路的例子。



Ana有5种不同的方法,如下表。

 
编程计算他在不超过T分钟买完东西并回到地点1的方案总数,由于答案较大,输出对5557求余后的结果。
输入:
第一行两个整数 N 和 M (1 ≤ N ≤ 25, 1 ≤ M ≤ 500), 表示点数和道路条数。.
以下 M 行 每行包涵两个整数u 和 v 和 1个字符串s,用1个空格分开。表示地点 u 到 v有一条单向路,且路上可买的东西是 s.
字符串 s 的长度为1到4且由大写字母构成。字符 'B' 表示flour,字符'J'表示eggs, 字符'M' 表示milk 以及字符 'P'表示 jam.
两点间最多有两条直接相连的有向路且,它们方向不相同。
最后一个整数T (1 ≤ T ≤ 1 000 000 000), 表示他同学还有T分才到达。
输出:
一个整数表示方法总数对 5557的余数.
SAMPLE TESTS
Input:
3 3
1 2 BMJ
2 3 MJP
3 1 JPB
5
Output:
3


题目大意:

从起点开始在给定时间内,获得4种物品,再回到起点的方案数


分析:

给定的时间很大,考虑矩阵乘法,我们记录一下起点被经历了多少次,那么答案就在这里面,
物品只有4个,可以二进制枚举
我们枚举出每种集合,代表只会得到这些
物品的走法的方案数,当然得到的方案最多只包含我们枚举的种类
但是不能保证全部包含,可能只有其中一两种物品
那么我们最后统计的次数就包含了含有最多我们枚举的物品种类的方案数
我们考虑1111的情况,也就是把所有的情况都有的情况,
那么它需要减去所有奇数的方案数,但多减了偶数的情况,再加上所以物品为偶数的情况
容斥一下 可以得到只包含1111的情况 就可以了


附上代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int mod=5557;

int n,m,time;
int cnt[16]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};

int u[501],v[501],ans[16];
char ch[501][4];
struct Matrix
{
    int a[56][56];
    void clear(){memset(a,0,sizeof(a));}
}A[16],T[16];


Matrix operator * (Matrix c,Matrix d)
{
    Matrix t;
    for(int i=0;i<=2*n;i++) 
        for(int j=0;j<=2*n;j++) 
        {
            t.a[i][j]=0;
            for(int k=0;k<=2*n;k++) 
            {
                t.a[i][j]+=c.a[i][k]*d.a[k][j];    
                if(t.a[i][j]>=mod) t.a[i][j]%=mod;
            }
        }
    return t;
}

bool check(char c,char s[])
{
    for(int i=0;i<4;i++) if(s[i] == c)return true;
    return false;
}

int qpow(Matrix c,Matrix d,int k)
{
    while(k)
    {
        if(k&1) c=c*d;
        d=d*d;
        k>>=1;
    }
    printf(" %d %d 
",c.a[0][0],c.a[0][2*n]);
    return ((c.a[0][0]+c.a[0][2*n]-1)+mod)%mod;
}

int solve()
{
    int ret=0;
    for(int i=0;i<16;i++) 
    {
        if(cnt[i]%2) ret-=ans[i];
        else ret+=ans[i];
    }
    ret=(ret%mod+mod)%mod;
    return ret;
}

int main()
{
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d%s",&u[i],&v[i],ch[i]),u[i]--,v[i]--;
    scanf("%d",&time);
    for(int i=0;i<16;i++) 
    {
        A[i].clear();
        T[i].clear();
        A[i].a[0][0]=1;
        T[i].a[0][2*n]++;//
        T[i].a[2*n][2*n]++;//记录开始点经过的次数 
        for(int j=0;j<n;j++) T[i].a[j+n][j]++;
        for(int j=1;j<=m;j++) 
        {
            T[i].a[u[j]][v[j]]++;
            if(!(i & 1) && check('B',ch[j]))continue;
            if(!(i & 2) && check('J',ch[j]))continue;
            if(!(i & 4) && check('M',ch[j]))continue;
            if(!(i & 8) && check('P',ch[j]))continue;
            if(i)T[i].a[u[j]][v[j] + n]++;
         }
         for(int j=0;j<4;j++) {if(i&(1<<j)) printf("1");else printf("0");}
             ans[i] = qpow(A[i],T[i],time);//求出每种路线的答案 
    }
    cout<<solve()<<endl;//容斥得到答案 
    
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Heey/p/9011268.html