2017暑期集训Day 9 递推

9 篇文章 0 订阅
2 篇文章 0 订阅

题目地址

A Tiling

[Solution]

递推的时候保证第n个方案是新方案即可,第n块可以使2*2,也可以是1*2的一块,亦或是1*2横过来的两块,这样

F[N] = 2 * F[n - 2] + F[n - 1]

Ps: 这道题目需要用到高精度,我是用了java的BigInteger来搞的,BigInteger是在math类下的包,另外java程序提

交的时候需要把class的名称改成Main,注意M大写,表示自己CE了好几发

[BigInteger]

about:BigInteger
define:
BigInteger[] f = new BigInteger[50];
BigInteger m = new BigInteger("1");
加法:BigInteger a;
a = a.add(b);
print:
System.out.println(a);
About:eof
Scanner sc = new Scanner(System.in);
while(sc.hasNext())
{
    n = sc.nextInt();
}

[Code]

import java.math.*;
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n;
        BigInteger[] f = new BigInteger[350];
        BigInteger m = new BigInteger("1");
        f[0] = m;
        f[1] = new BigInteger("1");
        for(int i = 2; i<= 250; i++) {
            BigInteger ans = f[i - 2].add(f[i - 2]);
            f[i] = f[i - 1].add(ans);
        }
        while(sc.hasNext()) {
            n = sc.nextInt();
            System.out.println(f[n]);
        }
    }
}

B 不容易系列之一

[problem]

n个人错位排序的方案数

[Solution]

设f[n]为n个人错位排序的方案数,考虑第k个人在第n个位置,此时剩下的人(包括第n个人)需要在前n-1的位置

排下来,但是此时结果并不是简简单单的f[n-1],因为第n个人排在哪个位置都是合法的,我们只需要讨论一下,我

们先假定当第n个人不能排在k位置的情况下,这样就满足了n-1个人错位排序,方案数为f[n-1],然后考虑第n个人

排在第k个位置的情况,此时剩下的n-2个人符合错位排序的情况,因此方案数为f[n-2],因此对于第k个人排在第n

个位置,会形成(f[n-1] + f[n - 2])种方案,而前n-1个人都可以排在第n个位置,因此

F[N] = (n - 1) * (F[N - 1] + F[N - 2])

[Code]


#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 100;
int n, p, k;
struct matrix{
    ll m[35][35];
};
ll f[100];
int main()
{
    //freopen("b.in", "r", stdin);
    f[1] = 0;
    f[2] = 1;
    for(int i = 3; i <= 20; i++)
        f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
    while(~scanf("%d",&n))
    {
        cout<<f[n]<<endl;
    }
    return 0;
}

C 一只小蜜蜂…

[Solution]

水题,大水题

[Code]

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define p 10000
typedef long long ll;
const int N = 10000;
ll f[100];
int main()
{
    //freopen("b.in", "r", stdin);
    int T;
    scanf("%d" ,&T);
    while(T--)
    {
        int x, y;
        scanf("%d%d",&x, &y);
        for(int i = x - 1; i <= y ; i++)
            f[i] = 0LL;
        f[x] = 1;
        for(int i = x + 1; i <= y; i++)
            f[i] = f[i - 1] + f[i - 2];
        cout<<f[y]<<endl;
    }
    return 0;
}

D fibonacci

[Solution]

斐波那契矩阵乘

[Code]

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define p 10000
typedef long long ll;
const int N = 10000;
struct matrix{
    ll m[15][15];
    matrix()
    {
        memset(m, 0, sizeof(m));
        for(int i = 1; i <= 2; i++)
            m[i][i] = 1LL;
    }
};
matrix mul(matrix a, matrix b)
{
    matrix c;
    for(int i = 1; i <= 2; i++)
        for(int j = 1; j <= 2; j++)
    {
        c.m[i][j] = 0;
        for(int k = 1; k <= 2; k++)
            c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % p) % p;
    }
    return c;
}
matrix fast(matrix a, int b)
{
    matrix c;
    while(b)
    {
        if (b % 2 == 1)
            c = mul(c, a);
        a = mul(a, a);
        b /= 2;
    }
    return c;
}
int main()
{
    //freopen("b.in", "r", stdin);
    int n;
    while(~scanf("%d", &n) && n >= 0)
    {
        matrix base;
        base.m[1][1] = 1;
        base.m[1][2] = 1;
        base.m[2][1] = 1;
        base.m[2][2] = 0;
        if (n == 0)
        {
            printf("0\n");
            continue;
        }
        base = fast(base, n);
        printf("%d\n", base.m[1][2]);
    }
    return 0;
}

E Nice Patterns Strike Back

[Problen]

给定N*M个格子,可以填成黑色和白色,但是不能出现2*2的相同颜色的方格,询问方案数。
N<= 10^100 ,M<= 5

[Solution]
好腻害的一道题目。

注意到M很小,这样每一行的状态数很少,只有2^5种,这样考虑从一行到下一行的状态之间的转移:

f[i][j] = ∑f[i-1][k], (k,j)相容,但是这样的dp会GG

然后把f[i][j] = ∑num * f[i -1][k], num为0或1

f[i][j] 是f[i -1]这一行与矩阵的第k列相乘得到的,因此f[i] 是f[i -1]与矩阵相乘得到的,而这个矩阵,不就是相容矩

阵么,即(i,j)相容则为1,否则为0

Ps:mmp,此题仍然需要高精度,手打c++高精…

[Code]

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 100;
int n, m, ss, tt, k, p;
struct biginteger{
  int len;
  int a[110];
  biginteger(string s)
  {
      len = s.length();
      int tot = 0;
      for(int i = s.length() -1; i >= 0; i--)
        a[++tot] = s[i] - '0';
  }
};
biginteger div( biginteger num)
{
    int delta = 0;
    for(int i = num.len; i >= 1; i--)
    {
        delta = delta * 10 + num.a[i];
        num.a[i] = delta / 2;
        delta = delta % 2;
    }
    while(num.len && num.a[num.len] == 0 )
        num.len--;
    return num;
}
biginteger sub(biginteger num)
{
    int tot = 1;
    while(num.a[tot] == 0)
    {
        num.a[tot] = 9;
        tot++;
    }
    num.a[tot] -- ;
    while(num.len && num.a[num.len] == 0)
        num.len--;
    return num;
}
struct matrix{
    ll m[55][55];
    matrix()
    {
        memset(m, 0, sizeof(m));
    }
};
matrix tran[15];
int b[15];
matrix mul(matrix a, matrix b)
{
    matrix c;
    for(int i = 1; i <= n; i++)
        c.m[i][i] = 1LL;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
    {
        c.m[i][j] = 0;
        for(int k = 1; k <= n; k++)
            c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % p) % p;
    }
    return c;
}
matrix fast(matrix a, biginteger b)
{
    matrix c;
    for(int i = 1; i <= n; i++)
        c.m[i][i] = 1LL;
    while(b.len)
    {
        if (b.a[1] % 2 == 1)
            c = mul(c, a);
        a = mul(a, a);
        b = div(b);
    }
    return c;
}
void pri(matrix a)
{
    printf("Matrix\n");
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j < n; j++)
            printf("%d ", a.m[i][j]);
        printf("%d\n", a.m[i][n]);
    }
}
bool judge(int x, int y, int len)
{
    if (x == n)
        x = 0;
    if (y == n)
        y = 0;
    int t1 = 0, t2 = 0;
    int a[10], b[10];
    for(int i = 1; i <= len; i++)
    {
        a[i] = 0;
        b[i] = 0;
    }
    while(x > 0)
    {
        a[++t1] = x % 2;
        x /= 2;
    }
    while(y > 0)
    {
        b[++t2] = y % 2;
        y /= 2;
    }
    for(int i = 1; i < len; i++)
        if (a[i] == b[i] && a[i] == a[i + 1] && a[i] == b[i + 1])
          return false;
    return true;
}
int main()
{
   // freopen("b.in", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        string s;
        cin>>s;
        scanf("%d%d", &m, &p);
        matrix base;
        biginteger mi(s);
        n = 1 << m;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= i; j++)
                if (judge(i , j, m))
            {
                base.m[i][j] = 1;
                base.m[j][i] = 1;
            }
        matrix ans;
        mi = sub(mi);
        ans = fast(base, mi);
        int answer = 0;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
              answer = (answer + ans.m[i][j]) % p;
        printf("%d\n", answer);
        if (T >= 1)
            printf("\n");
    }
    return 0;
}

F Matrix Power Series

[Problem]

Given a n*n matrix A and a positive integer k, find the sum S = A + A ^2 + A^ 3+…….+
A^k.
[Solution]
我们可以使用分治的思想来解决这个问题,首先把这个序列分成两部分,这样后一部分是前一部分的倍数,这样

问题可以通过解决前一半来解决。

[Ps]
我有好多mmp要讲,首先,矩阵乘的复杂度不小,会进行好多次乘法,这样long long 与int差距比较大,另外开

long long

乖乖地用cin,cout就好。。不要搞事情。。。一个因为long long wa、tle了一天的人如是说道
[Code]

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100;
int n, p, k;
struct matrix{
    int m[35][35];
};
matrix one;
matrix mul(matrix a, matrix b)
{
    matrix c;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
    {
        c.m[i][j] = 0;
        for(int k = 1; k <= n; k++)
            c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % p) % p;
    }
    return c;
}
matrix add(matrix a, matrix b)
{
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
          a.m[i][j] = (a.m[i][j] + b.m[i][j]) % p;
    return a;
}
matrix fast(matrix a, int b)
{
    matrix c;
    c = one;
    while(b)
    {
        if (b % 2 == 1)
            c = mul(c, a);
        a = mul(a, a);
        b /= 2;
    }
    return c;
}
matrix cal(matrix a, int b)
{
    if (b == 1)
        return a;
    if (b % 2 == 0)
    {
        matrix ans = cal(a, b / 2);
        matrix tot = add(fast(a, b / 2), one);
        return mul(ans, tot);
    }
    else
        return add(cal(a, b - 1),  fast(a, b));
}
int main()
{
   //freopen("b.in", "r", stdin);
    scanf("%d%d%d", &n, &k, &p);
    for(int i = 1; i <= n; i++)
        one.m[i][i] = 1;
    matrix a;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
          {
              scanf("%d", &a.m[i][j]);
              a.m[i][j] %= p;
          }
    matrix sum = cal(a, k);
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j< n; j++)
            printf("%d ", sum.m[i][j]);
        printf("%d\n", sum.m[i][n]);
    }
    return 0;
}

G - 233 Matrix

[Problem]

给定一个矩阵的第一行,第一列的值,f[i][j] = f[i - 1][j] + f[i][j - 1],询问n行m列的值

n<= 10 m <= 10^9

[Solution]

二维的递推式,另外对于每对(i, j), 只与i-1, j-1,二维的递推可以通过矩阵乘来解决,我们可以观察到n很小,每一列至多10个数,这样我们一列一列地向右转移,f[i][j] 的f[i][j - 1]这一部分我们可以在矩阵的对应第i行的第j列标记乘1,及保留上一列的数值,另外的f[i - 1][j]这一部分可以直接把构造矩阵的第i-1行累加起来即可

[Code]

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define p 10000007
typedef long long ll;
const int N = 100;
ll a[N], sum[N];
int n, m;
struct matrix{
    ll m[15][15];
    matrix()
    {
        memset(m, 0, sizeof(m));
        for(int i = 1; i <= n + 2; i++)
            m[i][i] = 1LL;
    }
};
matrix mul(matrix a, matrix b)
{
    matrix c;
    for(int i = 1; i <= n + 2; i++)
        for(int j = 1; j <= n + 2; j++)
    {
        c.m[i][j] = 0;
        for(int k = 1; k <= n + 2; k++)
            c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % p) % p;
    }
    return c;
}
matrix fast(matrix a, int b)
{
    matrix c;
    while(b)
    {
        if (b % 2 == 1)
            c = mul(c, a);
        a = mul(a, a);
        b /= 2;
    }
    return c;
}
int main()
{
    //freopen("b.in", "r", stdin);
    while(~scanf("%d%d", &n ,&m))
    {
        a[0] = 0LL;
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        sum[0] = 233LL;
        for(int i = 1; i <= n; i++)
            sum[i] = (sum[i - 1] + a[i]) % p;
        sum[n + 1] = 3LL;
        if (m == 0)
        {
            cout<<a[n]<<endl;
            continue;
        }
        if (m == 1)
        {
            cout<<sum[n]<<endl;
            continue;
        }
        matrix base;
        for(int i = 1; i <= n + 1; i++)
        {
           base.m[i][1] = 10LL;
           base.m[i][n + 2] = 1LL;
        }
        for(int i = 2; i <= n + 1; i++)
           for(int j = 2; j <= i; j++)
             base.m[i][j] = 1LL;
        base = fast(base, m - 1);
        ll ans = 0LL;
        for(int i = 1; i <= n + 2; i++)
            ans = (ans + (base.m[n + 1][i] * sum[i - 1] ) % p) % p;
        cout<<ans<<endl;
    }
    return 0;
}

F Matrix Power Series

[Problem]

给定一个无向图,询问从s到t的长度为k的通路,但是在某个时刻一些点是无法达到的,无法达到的点具有周期性,周期至多12

[Solution]

显然,每次增加一条通路,就是乘以转移矩阵,但是由于有事一些点无法到达,我们把转移矩阵的这一列标记成0即可,至多有12个转移矩阵,我们先累乘一下,然后算出最终答案是累乘矩阵的多少次幂,最终把%余下来的累成进去即可

[Code]

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define p 10000
typedef long long ll;
const int N = 100;
int n, m, ss, tt, k;
struct matrix{
    ll m[55][55];
    matrix()
    {
        memset(m, 0, sizeof(m));
        for(int i = 1; i <= n; i++)
            m[i][i] = 1LL;
    }
};
matrix tran[15];
int b[15];
matrix mul(matrix a, matrix b)
{
    matrix c;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
    {
        c.m[i][j] = 0;
        for(int k = 1; k <= n; k++)
            c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % p) % p;
    }
    return c;
}
matrix fast(matrix a, int b)
{
    matrix c;
    while(b)
    {
        if (b % 2 == 1)
            c = mul(c, a);
        a = mul(a, a);
        b /= 2;
    }
    return c;
}
void pri(matrix a)
{
    printf("Matrix\n");
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j < n; j++)
            printf("%d ", a.m[i][j]);
        printf("%d\n", a.m[i][n]);
    }
}
int main()
{
    //freopen("b.in", "r", stdin);
    scanf("%d%d%d%d%d", &n, &m, &ss, &tt, &k);
    ss++;  tt++;
    matrix a;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
           a.m[i][j] = 0;
    for(int i = 1; i <= m; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        x++;
        y++;
        a.m[x][y] = 1;
        a.m[y][x] = 1;
    }
    int nfish;
    scanf("%d", &nfish);
    for(int i = 1; i <= 12; i++)
        tran[i] = a;
    while(nfish--)
    {
        int tot;
        scanf("%d", &tot);
        scanf("%d", &b[0]);
        for(int i = 1; i < tot; i++)
            scanf("%d", &b[i]);
        for(int i = 0; i < tot; i++)
            b[i]++;
        for(int i = 1; i <= 12; i++)
        {
            int res = b[i % tot];
            for(int j = 1; j <= n; j++)
                tran[i].m[j][res] = 0;
        }
    }
    matrix sum;
    for(int i = 1; i <= 12; i++)
        sum = mul(sum, tran[i]);
    matrix ans;
    ans = fast(sum, k / 12);
   // pri(a);
    for(int i = 1; i <= k % 12; i++)
        {
            ans = mul(ans, tran[i]);
        //    pri(ans);
        }
    cout<<ans.m[ss][tt];
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值