[报告]ZJU 3651 Cipher Lock

Abstract

ZJU 3651 Cipher Lock

组合计数 DP 矩阵乘法

Body

Source

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3651

Description

N个点围成一个环,每个点有两个属性(u, v),取值范围分别是[1, p]和[1, q]。已知其中8个点的属性,问有多少个环,满足相邻两点至少有一个属性值相同。

Solution

很明显的用矩阵加速的dp统计题。

一开始想错状态,把起点的情况也考虑进去,弄了个3x3({us,ut,其他}x{vs,vt,其他})=9维的矩阵,然后还要考虑vs,vt相等或者us,ut相等之类的情况,写了一下午,一脸血。

后来观察了下,发现合并后,其实跟起点的情况没有关系。

首先把点按照坐标排序,算相邻点之间的方案数,最后乘起来即可。

对于相邻的点,记f(n,{0,1},{0,1})表示从起点开始走n个点,u和终点的u是否相等,v和终点的v是否相等的方案数。转移比较容易。由于n很大所以要用矩阵乘法加速。

注意有点坐标相同的情况,特殊判断一下。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

const ll mod = 1234567890ll;

struct sm {
    ll m[4][4];
    void init() {memset(m, 0, sizeof m);}
    sm() {init();}
    void eye() {for (int i = 0; i < 4; ++i) m[i][i] = 1;}
};

sm operator*(const sm &a, const sm &b) {
    sm res;
    for (int i = 0; i < 4; ++i)
        for (int j = 0; j < 4; ++j)
            for (int k = 0; k < 4; ++k)
                if (a.m[i][k] && b.m[k][j])
                    res.m[i][j] = (res.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
    return res;
}

sm operator^(sm x, ll p) {
    sm res;
    res.eye();
    for (; p; p>>=1) {
        if (p&1) res = res*x;
        x = x*x;
    }
    return res;
}

struct snode {
    ll x, u, v;
    void read() {cin>>x>>u>>v; x--; u--; v--;}
    bool operator<(const snode &rhs) const {
        return x < rhs.x;
    }
}node[10];

ll N, p, q;

ll solve() {
    int i, j, k;
    ll res = 1;
    sort(node, node+8);
    node[8] = node[0];
    node[8].x += N;
    sm m;
    m.m[0][0]=m.m[0][1]=m.m[0][2]=m.m[1][3]=m.m[2][3] = 1;
    m.m[1][0]=m.m[1][1]=m.m[3][2] = (q-1)%mod;
    m.m[2][0]=m.m[2][2]=m.m[3][1] = (p-1)%mod;
    m.m[3][3] = p>1&&q>1?(p+q-3)%mod:0;
    //from i to i+1
    for (i = 0; i < 8; ++i) {
        if (node[i].x==node[i+1].x) {
            if (node[i].u^node[i+1].u || node[i].v^node[i+1].v) return 0;
            else continue;
        }
        sm now = m^(node[i+1].x-node[i].x);
        j = ((node[i].u!=node[i+1].u)<<1)|(node[i].v!=node[i+1].v);
        res = (res*now.m[0][j])%mod;
    }
    res %= mod;
    if (res<0) res += mod;
    return res;
}

int main() {
    int i, j, k;
    while (cin>>N>>p>>q) {
        for (i = 0; i < 8; ++i)
            node[i].read();
        cout<<solve()<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/jffifa/p/2710021.html