非精写版-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)
x≡1(mod 3)
x
≡
0
(
m
o
d
5
)
x \\equiv 0 (mod \\ 5)
x≡0(mod 5)
x
≡
0
(
m
o
d
7
)
x \\equiv 0 (mod \\ 7)
x≡0(mod 7)
x
≡
0
(
m
o
d
3
)
x \\equiv 0 (mod \\ 3)
x≡0(mod 3)
x
≡
1
(
m
o
d
5
)
x \\equiv 1 (mod \\ 5)
x≡1(mod 5)
x
≡
0
(
m
o
d
7
)
x \\equiv 0 (mod \\ 7)
x≡0(mod 7)
x
≡
0
(
m
o
d
3
)
x \\equiv 0 (mod \\ 3)
x≡0(mod 3)
x
≡
0
(
m
o
d
5
)
x \\equiv 0 (mod \\ 5)
x≡0(mod 5)
x
≡
1
(
m
o
d
7
)
x \\equiv 1 (mod \\ 7)
x≡1(mod 7)
由三组式子分别计算出
(
5
⋅
7
⋅
x
)
%
3
=
=
0
(5·7·x)\\%3==0
(5⋅7⋅x)%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)
(35⋅2⋅2+21⋅1⋅3+15⋅1⋅2) mod (3⋅5⋅7)最终的最小值为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背包问题。 以上是关于非精写版-51nod基础训练的主要内容,如果未能解决你的问题,请参考以下文章
二维dp可以简化成一维。
每个物品选择选或者不选,根据背包内的容量维护出最大值。
状态转移方程:
d
p
[
j
]
=
m
a
x
{
d
p
[
j
−
w
]
+