10.10考试总结

矩阵乘法

(matrixf.cpp,1s,256MB)
【问题描述】
给定三个n*n的矩阵A,B,C,判断C是否等于A×B。
【输入格式】
输入文件包含多组数据。第一行是一个整数T(T<=5)表示测试点个数。
对于每组数据,第一行是一个正整数n,表示矩阵大小。
接下来3*n行,每行n个整数,分别表示A、B和C。
【输出格式】
对于每组数据,如果C=A×B,输出”Yes”,否则输出”No”。
【样例输入】
2
1
2
3
5
2
2 3
3 5
0 1
1 1
3 5
5 8
【样例输出】
No
Yes
【提示】
一个 nt 的矩阵A乘以一个 tm 的矩阵B,答案是一个 nm 的矩阵C,且满足 c[i,j]=a[i,k]b[k,j] ,其中1<=k<=t。即,矩阵C的第i行第j列的元素等于把矩阵A的第i行于矩阵B的第j列的元素一一对应相乘后作和。注意,矩阵乘法满足结合律但是不满足交换律,即A×B不一定等于B×A。但矩阵乘法满足结合律,即 ABC=A(BC)

【数据规模与约定】
对于20%的数据,n=1
对于60%的数据,n<=100
对于100%的数据,1<=n<=1000
矩阵A和矩阵B中的元素为小于1000的非负整数。
矩阵C中的元素在int范围内

要判断 A×B=C ,可以rand出一个 1n 的矩阵r,则一定有 r×A×B=r×C ,因为矩阵乘法满足结合律,那么就有 (r×A)×B=r×C 算出 r×A 在乘上 B 判断是否等于r×C就可以了
思想大概就是把两个 nn 的矩阵相乘转化成了 1n 的矩阵和 nn 的矩阵相乘,时间复杂度为 n2

代码如下:

#include<cstdlib>
#include<ctype.h>
#include<cstdio>
#define N 1005
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;}while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
inline int min(int a,int b){return a<b?a:b;}
int T,n;
int r[N],d1[N],d2[N],d3[N];
struct Matrix{
    int a[N][N];
    inline void read_Matrix(int k){
        for(int i=1;i<=k;i++)
            for(int j=1;j<=k;j++)
                a[i][j]=read();
    }
}a,b,c;
inline void mul(int a[],int b[N][N],int c[]){
    for(int i=1;i<=n;i++){
        c[i]=0;
        for(int j=1;j<=n;j++)
            c[i]+=a[j]*b[j][i];
    }
}
inline bool check(){
    mul(r,a.a,d1);
    mul(d1,b.a,d2);
    mul(r,c.a,d3);
    for(int i=1;i<=n;i++)
        if(d3[i]!=d2[i]) return false;
    return true;
}
main(){
    srand(19260817);
    for(int i=1;i<=1000;i++) r[i]=rand();//随机矩阵
    T=read();
    while(T--){
        n=read();
        a.read_Matrix(n);b.read_Matrix(n);c.read_Matrix(n);
        if(check()) puts("Yes");
        else puts("No");
    }
return 0;
}

数字游戏

(num.cpp, 1s,256MB)
问题描述
有n+1个数,分别为a0,a1,a2,…,an。小K正在用这n+1个数玩游戏,首先小K会把数字a0放中间,第i次操作他会将ai放在左端或者右端。我们知道小K有2n种方法摆放这些数字。
之后小K会对这2n种方法摆放方法求一个评估值,一种摆放方法的评估值为所有相邻两数的乘积之和。
小K想知道这2n种方法摆评估值之和为多少,由于答案太大,你只需要告诉他模109+7的结果。
输入格式
第一行一个正整数n。
接下来一行有n+1个正整数,分别表示a0,a1,a2,…,an。
输出格式
一行一个正整数,表示答案。
输入样例
2
1 2 1
输出样例
14
数据范围
对于30%的数据 ,1<=n<=10。
对于60%的数据 ,1<=n<=1000。
对于100%的数据 ,1<=n<=100000,ai<=109。

注意以下题解与代码都是按 1n+1 来写的

定义 fi 表示序列中前 i 个数能产生的最大乘积之和,则有

fi=fi1×2+a1×ai×2+Σ2ji1jai×aj×2j1

n2 算法:

因为当 j1 时,让 j 在最边上的状态一共有2j1个,则 aiaj 相邻的状态数一共有这么多个,产生的贡献为 ai×aj×2j1
j=1 1 在最边上的状态一共有两个,再加上a1×ai×2即可
最后再加上两个 fi1 (互相对称)即为当前所求的答案

把2的幂预处理出来就可以变成 O(n2) 的算法,60pts

代码如下:

#include<ctype.h>
#include<cstdio>
#define N 100050
#define MOD 1000000007
#define int long long
using namespace std;
typedef long long ll;
inline int read(){
    int x;
    scanf("%lld",&x);
    return x;
}
int n;
int a[N];
ll f[N];
inline int quick_pow(ll x,ll k){
    ll sum=1;
    while(k){
        if(k&1) sum=sum*x%MOD;
        x=x*x%MOD;
        k>>=1;
    }
    return sum;
}
inline void solve(){
    for(int i=2;i<=n;i++){
        for(int j=2;j<i;j++){
            f[i]=(f[i]+a[i]*a[j]*quick_pow(2,j-1)%MOD)%MOD;
        }
        f[i]=(f[i]+a[i]*a[1]*2%MOD)%MOD;
        f[i]=(f[i]+f[i-1]*2%MOD)%MOD;
    }
    printf("%lld\n",f[n]);
}
main(){
    n=read()+1;
    for(int i=1;i<=n;i++) a[i]=read();
    solve();
return 0;
}

O(n)优化:

发现每个数对应的 2j1 是一定的,所以可以搞个前缀和记录所有应该乘的 aj×2j1 的和就可以了

代码如下:

#include<ctype.h>
#include<cstdio>
#define N 100050
#define MOD 1000000007
#define int long long
using namespace std;
typedef long long ll;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;}while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n;
int a[N];
ll f[N],q[N];
inline int quick_pow(int x,int k){
    int sum=1;
    while(k){
        if(k&1) sum=sum*x%MOD;
        x=x*x%MOD;
        k>>=1;
    }
    return sum;
}
inline void solve(){
    for(int i=2;i<=n;i++){
        q[i]=(q[i-1]+a[i]*quick_pow(2,i-1))%MOD;//2的幂可以预处理出来
        f[i]=(f[i]+a[i]*q[i-1]%MOD)%MOD;
        f[i]=(f[i]+a[i]*a[1]*2%MOD)%MOD;
        f[i]=(f[i]+f[i-1]*2%MOD)%MOD;
    }
    printf("%lld\n",f[n]);
}
main(){
    n=read()+1;
    for(int i=1;i<=n;i++) a[i]=read();
    solve();
return 0;
}

物以类聚

(kind.cpp,1s,256MB)

【问题描述】
吉林省OIER们都喜欢吃糖,所以冬令营组委会在地上摆了n个各种各样的糖,标号为1……n,OIER们特别想知道在标号L到标号R之间共有种类为k的糖有多少块。
【输入格式】
从文件kind.in中输入数据。
第 1 行:两个空格隔开的正整数 n 和 m
第 2 行:n 个空格隔开的整数,第 i 个整数为第 i 个糖果的种类
接下来 m 行,每行三个整数 L,R,k 描述一个询问
【输出格式】
输出到文件kind.out中。
m 行每行一个整数对应每个询问的答案。
【样例输入】
5 3
1 2 3 2 3
1 3 1
2 4 2
3 4 3
【样例输出】
1
2
1
【数据规模与约定】
对于 50%的数据, n,m<=2000
对于 100%的数据, n,m<=100000,k在int范围内。

简单的离线处理,方法类似于BZOJ[3626]
就是把每个问题 lr 拆成 1r 1l1 ,记录下来排个序从头到尾扫一遍,放回原来的位置,颜色的记录离散化一下就可以了

注意可能有询问的颜色k不存在于原数列中,离散化后可能出问题,要开个set判断是否出现

代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<set>
#define N 100020
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;}while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
set<int>s;
set<int>::iterator it;
int n,m,nn,l,r,k,top;
int a[N],num[N],b[N],ans[N][3],c[N];
struct Query{
    int x,k,belong,num;
    Query(int _x=0,int _k=0,int _belong=0,int _num=0):x(_x),k(_k),belong(_belong),num(_num){}
}q[N<<1];
inline bool cmp(Query a,Query b){return a.x<b.x;}
main(){
    n=read();m=read();
    for(int i=1;i<=n;i++) c[i]=b[i]=a[i]=read();
    sort(b+1,b+n+1);
    sort(c+1,c+n+1);
    nn=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++){
        s.insert(a[i]);
        a[i]=lower_bound(b+1,b+nn+1,a[i])-b;
    }
    for(int i=1;i<=m;i++){
        l=read();r=read();k=read();
        it=s.lower_bound(k);
        if(*it!=k) continue;//判断是否合法
        k=lower_bound(b+1,b+nn+1,k)-b;
        q[++top]=Query(l-1,k,i,1);
        q[++top]=Query(r,k,i,2);
    }
    sort(q+1,q+top+1,cmp);
    int pre=1;
    for(int i=0;i<=n;i++){
        num[a[i]]++;
        while(q[pre].x==i){
            ans[q[pre].belong][q[pre].num]=num[q[pre].k];
            pre++;
        }
    }
    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i][2]-ans[i][1]);
    }
return 0;
}

4、矩阵

 (matrix.cpp,1s ,256MB)

问题描述
定义矩阵A= 的行列式为det(A)=ad-bc。
我们称行列式为0的矩阵为奇异矩阵。
现在给出一个矩阵A,要求一个奇异矩阵B,使得A-B中的元素的中绝对值最大值最小。
输入格式
第一行两个整数a,b
第二行两个整数c,d
输出格式
输出矩阵A-B中的绝对值最大的元素的绝对值最小。
只要答案与标准答案不成果10-3即可,输出的小数位数不要超过15位。
输入样例
1 2
3 4
输出样例
0.200000000000
样例解释
这里写图片描述
数据范围
对于100%的数据,|a|,|b|,|c|,|d|<=109

题解:
这里写图片描述
这个为什么是对的yy一下就好了吧..

代码如下:

#include<algorithm>
#include<cstdlib>
#include<ctype.h>
#include<cstdio>
#define eps 1e-5
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;}while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
double l,r,mid,minad,maxad,maxbc,minbc,ans,a,b,c,d;
main(){
    a=read();b=read();c=read();d=read();
    l=0;r=1000000000ll;
    while(l<=r){
        mid=(l+r)/2;
        maxad=max((a+mid)*(d+mid),max((a-mid)*(d-mid),max((a-mid)*(d+mid),(a+mid)*(d-mid))));
        minad=min((a+mid)*(d+mid),min((a-mid)*(d-mid),min((a-mid)*(d+mid),(a+mid)*(d-mid))));
        maxbc=max((b+mid)*(c+mid),max((b-mid)*(c-mid),max((b-mid)*(c+mid),(b+mid)*(c-mid))));
        minbc=min((b+mid)*(c+mid),min((b-mid)*(c-mid),min((b-mid)*(c+mid),(b+mid)*(c-mid))));
        if((maxbc>=minad&&minbc<=minad)||(minad<=minbc&&maxad>=minbc)) ans=mid,r=mid-eps;
        else l=mid+eps;
    }
    printf("%.8lf",ans);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值