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;
}