0
点赞
收藏
分享

微信扫一扫

ZJUT2021 第二学期 第二次 基础专题1[1]

young_d807 2022-02-12 阅读 31

目录

A - 矩阵快速幂

洛谷P3390 【模板】矩阵快速幂

 CodeForces 185A Plant      ​

 POJ 3233 Matrix Power Series

B - 尺取法 

POJ 3061 Subsequence

POJ 2100  Graveyard Design


typedef long long ll;
//快速幂返回x^n对mod取模的结果
ll mod_pow(ll x, ll n, ll mod) {
	ll res = 1;
	while (n > 0) {
		if (n & 1)res = res * x % mod;
		x = x * x % mod;
		n >>= 1; 
	}
	return res;
}
#include<iostream>
using namespace std;
typedef long long ll;

int l, mod = 1e9 + 7;
ll n;

struct Matrix {
	ll a[105][105] = { 0 };
}input;

Matrix multiply(Matrix A, Matrix B) {  //计算方阵A的平方的函数
	int i, j, k;
	Matrix ans;
    for (int i = 1; i <= l; ++i){
        for (int j = 1; j <= l; ++j){
            if (A.a[i][j]){
                for (int k = 1; k <= l; ++k){
                    ans.a[i][k] = (ans.a[i][k] + A.a[i][j] * B.a[j][k] % mod) % mod;
                }
            }
        }
    }
    return ans;
}

Matrix qpow(Matrix A, long long b) {  //矩阵快速幂实现函数,完全借鉴快速幂思想;
    Matrix ans;
    for (int i = 1; i <= l; i++)ans.a[i][i] = 1;
    while (b) {
        if (b & 1) ans = multiply(ans, A);
        A = multiply(A, A);
        b >>= 1;
    }
    return ans;
}

int main() {  //主函数,输入矩阵,快速幂处理,输出矩阵
    cin >> l >> n;
    for (int i = 1; i <= l; i++)
        for (int j = 1; j <= l; j++)
            cin >> input.a[i][j];
    input = qpow(input, n);
    for (int i = 1; i <= l; i++) {
        for (int j = 1; j <= l; j++)
            cout << input.a[i][j]<<" ";
        cout << endl;
    }  
}

矩阵快速幂实战:

      CodeForces 185A Plant      \binom{a_{n}}{b_{n}}=\begin{pmatrix} 3 &1 \\ 1 & 3 \end{pmatrix}\binom{a_{n-1}}{b_{n-1}}

      POJ 3233 Matrix Power Series      \binom{S[k]}{A^{k}}=\begin{pmatrix} E &A\\ O & A \end{pmatrix}\binom{S[k-1]}{A^{k-1}}   


#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;

int n, k, mod ;

struct Matrix {  //定义矩阵结构体
    int r, c;
    ll a[105][105] ;
}A,K,Init,res;

Matrix multiply(Matrix A, Matrix B) {  //矩阵A,B相乘模板,适用于所有矩阵;
    int i, j, k;
    Matrix ans;
    memset(ans.a,0,sizeof(ans.a));
    ans.r = A.r; ans.c = B.c;
    for (int i = 1; i <= A.r; ++i) {
        for (int j = 1; j <= A.c; ++j) {
            if (A.a[i][j]) {
                for (int k = 1; k <= B.c; ++k) {
                    ans.a[i][k] = (ans.a[i][k] + A.a[i][j] * B.a[j][k] % mod) % mod;
                }
            }
        }
    }
    return ans;
}
Matrix qpow(Matrix A, long long b) {  //矩阵快速幂函数
    Matrix ans;
    ans.r = ans.c = 2 * n;
    memset(ans.a,0,sizeof(ans.a));
    for (int i = 1; i <= A.c; i++)ans.a[i][i] = 1;
    while (b) {
        if (b & 1) ans = multiply(ans, A);
        A = multiply(A, A);
        b >>= 1;
    }
    return ans;
}
int main() {  //输入待处理矩阵 -> 构造系数矩阵,初始矩阵 ->系数矩阵快速幂 -> K*Init得到答案
    cin >> n >> k >> mod;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> A.a[i][j];
    A.r = A.c = n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i == j)K.a[i][j] = 1;
            else K.a[i][j] = 0;
    for (int i = n + 1; i <= 2 * n; i++)
        for (int j = 1; j <= n; j++)
            K.a[i][j] = 0;
    for (int i = 1; i <= 2 * n; i++)
        for (int j = n + 1; j <= 2 * n; j++)
            K.a[i][j] = A.a[(i-1) % n+1][j - n];
    K.r = K.c = 2 * n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            Init.a[i][j]=0;
    for (int i = n+1; i <= 2*n; i++)
        for (int j = 1; j <= n; j++)
            if (i-n == j)Init.a[i][j] = 1;
            else Init.a[i][j] = 0;
    Init.r=2*n;Init.c=n;
    K=qpow(K,k);
    res = multiply(K, Init);
    for (int i = 1; i <=  n; i++) {
        for (int j = 1; j <=  n; j++)
            cout << res.a[i][j] << " ";
        cout << endl;
    }
}

算法Tips: 关于快速幂:数在计算机内以二进制保存,这就表示对于任何自然数 n,一定能表示成 n = 2^{k_{1}} + 2^{k_{2}} + ... + 2^{k_{n}} 的形式,其中 k_{n}={​{0,1,2,3...}}。例:22 = 2 + 4 + 16 ;于是有 x^{22} = x^{2} * x^{4} * x^{16}。这样,枚举 x^{2^{n}} ,并通过对幂数位运算判断是否需要相乘,这样就能将原本时间复杂度为 O(n)的乘幂算法降为 O(logn)。


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

int sum, nsum,a[100005];  //sum记录目标总和,nsum记录当前总和,a[]记录数组
int  n,index, len;  //len记录最短长度

void count() {
    len = n +1;
	index = 0;
    for (int i = 0; i < n; i++){  //从下标为i的数开始尺取;
        while (index < n && nsum < sum){
            nsum += a[index];
			index++;
        }
        if (nsum >= sum) len = min(len, index - i );  //找满足条件的最小长度
        nsum -= a[i];  //去掉开头的数,下一次循环将从当前所取数段的第二个数开始取
    }
}
int main() { //输入 -> 尺取 -> 输出答案
	int k;
	cin >> k;
	while (k--) {
		nsum = 0;
		cin >> n >> sum;
		for (int i = 0; i < n; i++) {
            cin >> a[i];
		}
		count();
		cout << (len == n +1 ? 0 : len) << endl;
	}
}


#include<iostream>
#include<vector>
using namespace std;
typedef pair<int, int>p;
typedef long long ll;
ll sz, sum, sq,n;
ll be = 1, en = 1;
vector<p>ans;
p p1;

int main() {
    cin >> n;
    while (1)
    {
        while (sum < n) {  //从be的位置尺取一段平方和大于等于n的数列
            sq = en * en;
            sum += sq;
            en++;
        }
        if (sq > n) break;  //最大的平方数比目标数大,不可能再有答案
        if (sum == n)  //找到一种可能解,加入答案数列
        {
            p1.first = be; p1.second = en;
            ans.push_back(p1);
        }
        sum -= be * be;  //尺取开头的位置+1;
        be++;
    }
    sz = ans.size();  //按照题目要求格式输出答案
    cout << sz << endl;
    for (int i = 0; i < sz; ++i) {
        int l = ans[i].first;
        int r = ans[i].second;
        cout << r - l << " ";
        for (int j = l; j < r; ++j) {
            cout << j << " ";
        }
        cout << endl;
    }
}

算法Tips:尺取法(双指针)的用途是比较广泛的。有的时候在一个On^2)的算法里面能够优化许多。简要做法:维护两个表示下标的指针leftright,代表当前区间的左右端点。让右端点不断右移,直到出现答案,然后左端点右移一次,重复上述操作直到右端点到头。显然,这样做必须保证答案在区间上的单调性。


举报

相关推荐

2021寒假第二次总结

第二次网页

第二次作业

HCIP第二次实验

第二次java学习

第二次java作业

网页前端第二次

0 条评论