zoukankan      html  css  js  c++  java
  • [报告]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;
    }
  • 相关阅读:
    vue教程2-06 过滤器
    vue教程2-05 v-for循环 重复数据无法添加问题 加track-by='索引'
    vue教程2-04 vue实例简单方法
    Linux文件I/O
    Linux内存管理
    进程中内存地址空间的划分
    Ubuntu12.04 15.04禁止移动介质自动播放
    条件编译,头文件,静态库,共享库与多文件编程
    C语言的函数
    C语言流程控制
  • 原文地址:https://www.cnblogs.com/jffifa/p/2710021.html
Copyright © 2011-2022 走看看