非精写版-51nod基础训练

Posted iuk11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了非精写版-51nod基础训练相关的知识,希望对你有一定的参考价值。

衔接上篇点击跳转
因为长度问题,一直发不出去,所以拆开来完成。

中国剩余定理

看了好几个博客,推导的式子都是错误的,额,越看越难受。最后在b站找到了一个讲解视频,之前就看到过,后来又忘记怎么算了,写下来记录一下。
栗子: 三三数余二,五五数余三,七七数余二,问数为多少。
翻译:
k % 3 = 2 k\\%3=2 k%3=2
k % 5 = 3 k\\%5=3 k%5=3
k % 7 = 2 k\\%7=2 k%7=2
问k最小为多少。
套用中国剩余定理的公式:
x ≡ 1 ( m o d   3 ) x \\equiv 1 (mod \\ 3) x1(mod 3)
x ≡ 0 ( m o d   5 ) x \\equiv 0 (mod \\ 5) x0(mod 5)
x ≡ 0 ( m o d   7 ) x \\equiv 0 (mod \\ 7) x0(mod 7)

x ≡ 0 ( m o d   3 ) x \\equiv 0 (mod \\ 3) x0(mod 3)
x ≡ 1 ( m o d   5 ) x \\equiv 1 (mod \\ 5) x1(mod 5)
x ≡ 0 ( m o d   7 ) x \\equiv 0 (mod \\ 7) x0(mod 7)

x ≡ 0 ( m o d   3 ) x \\equiv 0 (mod \\ 3) x0(mod 3)
x ≡ 0 ( m o d   5 ) x \\equiv 0 (mod \\ 5) x0(mod 5)
x ≡ 1 ( m o d   7 ) x \\equiv 1 (mod \\ 7) x1(mod 7)

由三组式子分别计算出 ( 5 ⋅ 7 ⋅ x ) % 3 = = 0 (5·7·x)\\%3==0 (57x)%3==0 的x最小值,其它两组同理。得到三个最小值为70、21、15。 ( 35 ⋅ 2 ⋅ 2 + 21 ⋅ 1 ⋅ 3 + 15 ⋅ 1 ⋅ 2 )   m o d   ( 3 ⋅ 5 ⋅ 7 ) (35·2·2+21·1·3+15·1·2) \\ mod \\ (3·5·7) (3522+2113+1512) mod (357)最终的最小值为23。
又看了巨佬的博客,写的超好。
在最后一步确定每个式子中的x的值时,采用了拓展欧几里得求解。(exgcd)
最后在根据公式把每个部分和在一起得到结果。(中国剩余定理)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105;
ll m[maxn],a[maxn],lcm=1;
int n;
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    ll res=exgcd(b,a%b,x,y),temp=x;
    x=y;
    y=temp-(a/b)*y;
    return res;
}
ll China(){
    ll d,x,y,res=0;
    for(int i=1;i<=n;i++) lcm=lcm*m[i];
    for(int i=1;i<=n;i++){
        ll kl=lcm/m[i];
        d=exgcd(kl,m[i],x,y);
        x=(x%m[i]+m[i])%m[i];
        res=(res+a[i]*x*kl)%lcm;
    }
    return res;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&m[i],&a[i]);
    }
    printf("%lld",China());
    //system("pause");
    return 0;
}

若所给的质数不互质,用扩展中国剩余定理(EX_CRT),解决模数可不互质的同余方程组。
替换掉上一段代码中的China()函数即可。

ll China(){
    ll M=m[1],A=a[1],t,d,x,y;
    for(int i=2;i<=n;i++){
        d=exgcd(M,m[i],x,y);
        if((a[i]-A)%d) return -1;
        x*=(a[i]-A)/d;
        t=m[i]/d;
        x=(x%t+t)%t;
        A=M*x+A;
        M=M/d*m[i];
        A%=M;
    }
    A=(A%M+M)%M;
    return A;
}

子段求和

可能数据弱吧,用前缀和给过掉了。
5e5*1e9的数据不知道会不会爆掉。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50050;
int n,q;
ll temp;
ll b[maxn];
int index,len;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>temp;
        b[i]=b[i-1]+temp;
    }
    cin>>q;
    while(q--){
        cin>>index>>len;
        cout<<b[index+len-1]-b[index-1]<<endl;
    }
    //system("pause");
    return 0;
}

树状数组做法:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
ll a[maxn],b[maxn];
int n,q;
int index,len;
int lowbit(int x){
    return x&-x;
}
void update(int i,int x){
    while(i<=n){
        b[i]+=x;
        i+=lowbit(i);
    }
}
ll sum(int x){
    ll res=0;
    while(x>=1){
        res+=b[x];
        x-=lowbit(x);
    }
    return res;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        update(i,a[i]);
    }
    cin>>q;
    while(q--){
        cin>>index>>len;
        int l=index,r=index+len-1;
        cout<<sum(r)-sum(l-1)<<endl;
    }
    //system("pause");
    return 0;
}

线段树做法:
(带懒惰标记的线段树模板)参考的该篇博客
我只用了建树和区间查找,但是下面是完整的线段树模板。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
ll a[maxn],ans[maxn<<2],lazy[maxn<<2];
int n,q;
int index,len;
void pushup(int rt){//更新节点信息
    ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}
void build(int l,int r,int rt){//建树
    if(l==r){
        ans[rt]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void pushdown(int rt,int ln,int rn){//下推懒惰标记
    //ln表示左子树元素结点个数,rn表示右子树结点个数
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        ans[rt<<1]+=lazy[rt]*ln;
        ans[rt<<1|1]+=lazy[rt]*rn;
        lazy[rt]=0;
    }
}
void add(int L,int C,int l,int r,int rt){//点更新
    if(l==r){
        ans[rt]+=C;
        return ;
    }
    int mid=(l+r)>>1;
    //pushdown(rt,mid-l+1,r-mid);//若既有点更新又有区间更新,就加上这句。
    if(L<=mid){
        add(L,C,l,mid,rt<<1);
    }else{
        add(L,C,mid+1,r,rt<<1|1);
    }
    pushup(rt);
}
void update(int L,int R,int C,int l,int r,int rt){//区间更新
    if(L<=l&&r<=R){
        ans[rt]+=C*(r-l+1);
        lazy[rt]+=C;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);
    if(L<=mid) update(L,R,C,l,mid,rt<<1);
    if(R>mid) update(L,R,C,mid+1,r,rt<<1|1);
    pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){//区间查询
    if(L<=l && r<=R){
        return ans[rt];
    }
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);
    ll ans=0;
    if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
    if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
    return ans;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,n,1);
    cin>>q;
    while(q--){
        cin>>index>>len;
        int l=index,r=index+len-1;
        cout<<query(l,r,1,n,1)<<endl;
    }
    //system("pause");
    return 0;
}
//建树 Build(1,n,1);   
//点更新 Add(L,C,1,n,1);  
//区间修改 Update(L,R,C,1,n,1);  
//区间查询 int ANS=Query(L,R,1,n,1);  

背包问题

背包九讲中的最简单问题:01背包问题。
二维dp可以简化成一维。
每个物品选择选或者不选,根据背包内的容量维护出最大值。
状态转移方程:
d p [ j ] = m a x { d p [ j − w ] +

以上是关于非精写版-51nod基础训练的主要内容,如果未能解决你的问题,请参考以下文章

非精写版-51nod基础训练

非精写版-51nod基础训练

非精写版-51nod基础训练(终)

非精写版-51nod基础训练(持续更新)

51nod_1459 最短路 dijkstra 特调参数

1057 N的阶乘(51NOD基础题)