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

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

中国剩余定理

看了好几个博客,推导的式子都是错误的,额,越看越难受。最后在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 ] + v , d p [ j ] } dp[j]= max\lbrace dp[j-w]+v,dp[j] \rbrace dp[j]=max{dp[jw]+v,dp[j]}

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10010;
ll dp[maxn];
struct node{
    int wi;
    int val;
}a[110];
int n,w;
int main(){
    cin>>n>>w;
    for(int i=1;i<=n;i++){
        cin>>a[i].wi>>a[i].val;
    }
    for(int i=1;i<=n;i++){
        for(int j=w;j>=a[i].wi;j--){
            dp[j]=max(dp[j-a[i].wi]+a[i].val,dp[j]);
        }
    }
    cout<<dp[w]<<endl;
    //system("pause");
    return 0;
}
最长回文子串(Manacher算法)

一下没缓过神来,看自己以前写的代码有点懵。
其实真的很简单。
Manacher首先有一个思想——所有的字符串都是奇数位的,怎么让它为奇数位呢,我们在每两个字符的中间插一个#,例如:abba–>a#b#b#a,这样构造出来的新字符串一定是奇数的。
讲清楚了前置问题,我们来引入正菜:
( 1 ) l e n [ i ] = m i n { l e n [ 2 ⋅ p o s − i ] , m a x l e n − i } (1)len[i]=min\{len[2·pos-i],maxlen-i\} (1)len[i]=min{len[2posi],maxleni}
( 2 ) l e n [ i ] = 1 (2)len[i]=1 (2)len[i]=1
( 3 ) 对 i 位 置 上 的 字 符 向 左 右 进 行 匹 配 , 更 新 l e n [ i ] 的 值 , 更 新 p o s , m a x l e n (3)对i位置上的字符向左右进行匹配,更新len[i]的值,更新pos,maxlen (3)ilen[i]pos,maxlen
p o s pos pos:当前最大的回文子串的中点。
m a x l e n maxlen maxlen:当前最大的回文子串的右边界。
我们有了三个操作,接下来根据具体情况选择需要执行哪项操作:
(1) i < m a x l e n i<maxlen i<maxlen 具体的说就是在[0,i-1]的时候,已经有一个下标pos的边界更新到了maxlen,现在我们要找的i还在之前的边界里。这样i在pos的右边,pos的左边一定存在一个j与i关于pos对称(这也是我们为什么要构造奇数字符串),这样的话有三种子情况:

  • 1 该串 l e n [ i ] = l e n [ j ] len[i]=len[j] len[i]=len[j]
  • 2 超出 m a x l e n − i maxlen-i maxleni
  • 3 恰好等于
    如果是1、2 的情况,长度的选择即为 l e n [ i ] = m i n { l e n [ 2 ⋅ p o s − i ] , m a x l e n − i } len[i]=min\{len[2·pos-i],maxlen-i\} len[i]=min{len[2posi],maxleni}
    如果是3的情况,我们没法确认maxlen外面还有没有能回文的字符,所以先把该位置标记为 l e n [ i ] = 1 len[i]=1 len[i]=1,然后再执行第3个操作,匹配一遍,得到长度。

(2) i > = m a x l e n i>=maxlen i>=maxlen

只能先执行操作2,再执行操作3.

#include<bits/stdc++.h>
using namespace std;
string s;
string longest(string s){
    string newstr="$#";
    for(int i=0;i<s.size();i++){
        newstr+=s[i];
        newstr+="#";
    }
    vector<int> len(newstr.size(),0);
    int pos=0,max=0;
    int start=0,maxlen=0;
    for(int i=1;i<newstr.size();i++){
        if(i<max){
            len[i]=min(len[2*pos-i],max-i);
        }else{
            len[i]=1;
        }
        while(i+len[i]<newstr.size() && i-len[i]>0 && 
            newstr[len[i]+i]==newstr[i-len[i]]){
            len[i]++;
        }
        if(i+len[i]>max){
            pos=i;
            max=i+len[i];
        }
        if(len[i]-1>maxlen){
            start=(i-len[i])/2;
            maxlen=len[i]-1;
        }
    }
    return s.substr(start,maxlen);
}
int main(){
    string p;
    getline(cin,s);
    p=longest(s);
    int len=p.size();
    cout<<len<<endl;
    //system("pause");
    return 0;
}
质数检验

质数n有性质:只能除以1和它本身,余数为0。暴力的判断方式就是在[2,n-1]一直做判断,如果有能被整除的数出现,说明它不是质数。简化一点的就是在[2,floor(sqrt(n))+1]一直做判断,我们分析这道题的数据范围为 2 < = S [ i ] < = 1 0 9 2 <= S[i] <= 10^9 2<=S[i]<=109,那么我们要先用质数筛(我用的线性筛,模板在蓝桥杯整理哪个博客中有)把需要的质数打表,( s q r t ( 1 0 9 ) < 1 0 5 sqrt(10^9)<10^5 sqrt(109)<105),我们的质数表打到 1 0 5 10^5 105即可,不会超时。为什么要用质数除n,举个例子,如果这个数不能被2整除,那肯定就不能被4整除,对于n去除2的倍数是毫无意义且浪费时间的事情,我们可以剪枝掉。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
int prime[maxn];
int vis[maxn];
ll x;
int Prim(int n){
	int p=0;
	vis[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			prime[++p]=i;
		}
		for(int j=1;j<=p&&i*prime[j]<=n;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) break;
		}
	}
	return p;
}
bool check(int len){
    for(int i=1;i<=len && prime[i]<x;i++){
        if(x%prime[i]==0) return false;
    }
    return true;
}
int main(){
    int n;
    cin>>n;
    int len=Prim(maxn);
    while(n--){
        cin>>x;
        if(check(len)) cout<<"Yes"<<"\n";
        else cout<<"No"<<"\n";
    }
    //system("pause");
    return 0;
}
机器人走方格

简单的二维递推式。题目明确规定只能向下或者向右走,所以(i,j)位置有多少种情况就是向上有多少种情况(i,j-1)+向左有多少种情况(i-1,j)的和。
数据范围: 2 < = m , n < = 1000 2 <= m,n <= 1000 2<=m,n<=1000

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1010;
const int mod=1e9+7;
int n,m;
ll dp[maxn][maxn];
int main(){
    cin>>n>>m;
    dp[1][1]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(i==1 && j==1) continue;
            dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
        }
    }
    cout<<dp[n][m]<<endl;
    system("pause");
    return 0;
}

数据范围: 2 < = m , n < = 1000000 2 <= m,n <= 1000000 2<=m,n<=1000000
在更大的数据下,上述的方法显然会超时(TLE),那么我们去寻找规律,可以发现,这是一个取模的组合数问题。
将上述的dp矩阵顺时针旋转45°,就会发现这就是一个杨辉三角形,那么自然而然可以想到用组合数来解决。
从起点(左上角)到终点(右下角)的距离就是我们组合数C(n,m)中的n, m i n { m − 1 , n − 1 } min\{m-1,n-1\} min{m1,n1}就是组合数中的m,距离如何计算,可以把起点到终点的路线进行平移,向右需要走n-1步,向下需要走m-1步,总共的 n = m + n − 2 n=m+n-2 n=m+n2 .
因为题目中要求取模,所以不能简单的分子分母做商,采用费马小定理,写成 f z ∗ q p o w ( f m , m o d − 2 ) fz*qpow(fm,mod-2)%mod fzqpow(fm,mod2)的形式,等同于不取模情况下的 f z / f m fz/fm fz/fm

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1010;
const int mod=1e9+7;
int n,m;
ll qpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll calc(ll n,ll m){
    ll fz=1;
    ll fm=1;
    for(ll i=n;i>=m+1;i--){
        fz=fz*i%mod;
    }
    for(ll i=1;i<=n-m;i++){
        fm=fm*i%mod;
    }
    return fz*qpow(fm,mod-2)%mod;
}
int main(){
    cin>>n>>m;
    cout<<calc(m+n-2,min(m-1,n-1));
    //system("pause");
    return 0;
}
最长递增子序列

一种新思想:

  • 1 a [ i ] > d p [ l e n ] a[i]>dp[len] a[i]>dp[len] a[i]是序列的第i个,dp[len]是选择的子序列的最后一个。在当前情况下,可以直接把a[i]加进子序列中。
  • 2 a [ i ] < = d p [ l e n ] a[i]<=dp[len] a[i]<=dp[len] 二分找到第一个大于等于a[i]的下标,把值替换成a[i]。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50050;
ll a[maxn];
ll dp[maxn];
int n;
int pos,len;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    dp[len]=a[1];
    for(int i=1;i<=n;i++){
        if(a[i]>dp[len]){
            dp[++len]=a[i];
        }else{
            pos=lower_bound(dp,dp+len,a[i])-dp;
            dp[pos]=a[i];
        }
    }
    cout<<len+1<<endl;
    return 0;
}
原根

a是p的原根,满足:p-1的所有质因子 p i p_i pi,都满足 a p − 1 / p i % p ! = 1 a^{p-1/p_i}\%p!=1 ap1/pi%p!=1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000005;
bool vis[N];
vector<int>p,b;
void init(){
    memset(vis,false,sizeof(vis));
    for(int i=2;i<=N;i++){
        if(!vis[i]){
            p.push_back(i);
            for(int j=i+i;j<=N;j+=i)vis[j]=true;
        }
    }
}
void getnum(ll n){
    int tmp=sqrt(n);
    for(int i=0;i<p.size();i++){
        if(n%p[i]==0){
            b.push_back(p[i]);
            while(n%p[i]==0)n/=p[i];
        }
    }
    if(n>1)b.push_back(n);
}
ll qpow(ll a,ll b,ll p){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%p;
        b>>=1;
        a=a*a%p;
    }
    return ans;
}
int main(){
    ll x;
    cin>>x;
    init();
    getnum(x-1);
    for(int i=2;i<x;i++){
        int flag=0;
        for(int j=0;j<b.size();j++){
            ll tmp=(x-1)/b[j];
            if(qpow(i,tmp,x)==1){
                flag=1;break;
            }
        }
        if(!flag){
            cout<<i<<endl;
            break;
        }
    }
    //system("pause");
    return 0;
}

不知道算day几了,断更几天,昨天写河南省赛重现了一下,发现状态好可以单挑一个银,等能参加了再说。该蓝桥杯国赛了,练练dp吧,基础等国赛完了之后再慢慢更全。


又回来更新了,为了国赛和一些作业耽搁了几天,继续更,准备省赛了。


欧拉函数

欧拉函数求解:对于n,小于等于n的数中,与n互质的数的数目。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll phi(ll n){
    ll res=n;
    for(ll i=2;i<=n/i;i++){
        if(n%i==0){
            res=res/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) res=res/n*(n-1);
    return res;
}
int main(){
    ll n;
    cin>>n;
    cout<<phi(n)<<endl;
    //system("pause");
    return 0;
}
矩阵乘法

写数组,写vector都行,用结构体比较好传参,简单明了。
矩阵乘法就是前行乘后列,用三维计算,得到结果输出即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
const int mod=1e9+7;
typedef long long ll;
struct Mat{
    ll m[maxn][maxn];
};
ll n;
Mat mul(Mat x,Mat y){
    Mat b;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            b.m[i][j]=0;
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            for(int k=0;k<n;k++){
                b.m[i][j]=(b.m[i][j]%mod+(x.m[i][k]*y.m[k][j])%mod)%mod;
            }
        }
    }
    return b;
}
int main(){
    Mat a,b;
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>a.m[i][j];
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>b.m[i][j];
        }
    }
    Mat c=mul(a,b);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cout<<c.m[i][j]<<" ";
        }
        cout<<endl;
    }
    //system("pause");
    return 0;
}
矩阵快速幂

特别注意:快速幂的 r e s res res矩阵,初始化成单位阵(主对角线为1,其余为0)

#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
const int mod=1e9+7;
typedef long long ll;
struct Mat{
    ll m[maxn][maxn];
};
ll n,m;
Mat mul(Mat x,Mat y){
    Mat b;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            b.m[i][j]=0;
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            for(int k=0;k<n;k++){
                b.m[i][j]=(b.m[i][j]%mod+(x.m[i][k]*y.m[k][j])%mod)%mod;
            }
        }
    }
    return b;
}
Mat qpow(Mat a,ll b){
    Mat res;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            res.m[i][j]=0;
            if(i==j) res.m[i][j]=1;
        }
    }
    while(b){
        if(b&1) res=mul(res,a);
        a=mul(a,a);
        b>>=1;
    }
    return res;
}
int main(){
    Mat a,c;
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>a.m[i][j];
        }
    }
    c=qpow(a,m);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cout<<c.m[i][j]<<" ";
        }
        cout<<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;
void pushup(int rt){//更新节点信息
    ans[rt]=max(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;
    }
}
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=max(ans,query(L,R,l,mid,rt<<1));
    if(R>mid) ans=max(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;
    ll l,r;
    while(q--){
        cin>>l>>r;
        cout<<query(l+1,r+1,1,n,1)<<endl;
    }
    //system("pause");
    return 0;
}

后话:求区间中第k大的数——主席树。

质数中的质数(质数筛法)

质数筛,用了埃氏筛,背起来简单。
用数组存一下质数,这样这个数组下标就是排第几的质数。
用一个数组存 [ 1 , m a x n ] [1,maxn] [1,maxn]中这个数是不是质数,为了后面判断,这样能直接判断出下标是不是质数

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
int prime[maxn];
int vis[maxn];
int Vis[maxn];
int Prim(int n){
	int p=0;
	memset(vis,0,sizeof(vis));
	vis[0]=vis[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			prime[++p]=i;
            Vis[i]=1;
			for(int j=i+i;j<=n;j+=i){
				vis[j]=1;
			}
		}
	}
	return p;
}
vector<int> ve;
int main(){
	int n;
	cin>>n;
	int num=Prim(maxn-100);
	for(int i=1;i<=num;i++){
		if(Vis[i]&&prime[i]>=n){
            cout<<prime[i]<<endl;
            break;
        }
	}
    //system("pause");
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值