复习Day3__

0蓝桥骑士 - 蓝桥云课 (lanqiao.cn)

//由于本题数据量较大,使用常规的O(n^2)的LIS算法会超时
//故本题采用二分+贪心的算法求解: 
//维护一个low数组,low[i]表示长度为i的最长上升子序列的末尾元素
//使用贪心思想来对low数组进行更新,核心思想是末尾元素越小,越容易凑出最长的子序列
//遍历每一个元素,若当前元素比low[i]更大,则直接加入low数组的末尾 
//若当前元素小于low[i],则从low数组中找到一个大于等于它的最小值将其替换
//由于low数组是递增的,故使用二分算法进行搜索和替换
//最终输出low数组的长度即为答案 
//例1:对于序列arr[8]={3,1,2,6,4,5,10,7}
//扫描arr[1],low[1]=3;扫描arr[2],low[1]替换为1;扫描arr[3],low[2]=2;扫描arr[4],low[3]=6
//扫描arr[5],low[3]替换为4;扫描arr[6],low[4]=5;扫描arr[7],low[5]=10;扫描arr[8],low[5]替换为7
//最终low数组长度为5,即为答案,此时low[5]={1,2,4,5,7}
//例2:对于序列arr[8]={1,4,7,2,5,9,10,3}
//扫描arr[1],low[1]=1;扫描arr[2],low[2]=4;扫描arr[3],low[3]=7;扫描arr[4],low[2]替换为2
//扫描arr[5],low[3]替换为5;扫描arr[6],low[4]=9;扫描arr[7],low[5]=10;扫描arr[8],low[3]替换为3
//最终low数组长度为5,即为答案,此时low[5]={1,2,3,9,10};
//注意此算法的缺陷:low数组只有长度是有意义的,其保存的元素是无意义的
//比如上例中的low={1,2,3,9,10},原序列中不存在此子序列
//实际上low数组中每个元素只代表一种排列可能性,不代表最终的排列结果 
#include <bits/stdc++.h>
using namespace std;
int arr[300009];
int low[300009];
int main(){
    int N;int length=1;
    cin>>N;
    for(int i=1;i<=N;i++)cin>>arr[i];    
    low[1]=arr[1];//初始化,长度为1的子序列初始化为第一个元素 
    for(int i=2;i<=N;i++)//依次遍历后面的元素,更新low数组 
    {
        if(arr[i]>low[length])//若当前元素大于low数组末尾元素,直接插入 
        {
            low[++length]=arr[i];
        }
        else{ // 当arr[i]介于low[1]和low[length]之间时,在low数组中进行二分查找并替换
    int l = 1, r = length;
    while (l < r){
        int mid = (l + r) / 2;
        if (low[mid] >= arr[i])
            r = mid;
        else
            l = mid + 1;
    }
    low[r] = arr[i]; // 将low数组中第一个大于等于arr[i]的元素替换为arr[i]
}
}
    cout<<length<<'\n';//输出low数组长度即为答案 
    return 0;
}

0最长公共子序列 - 蓝桥云课 (lanqiao.cn)

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+6;
int a[N],b[N];
int n,m;
int dp[N][N];//dp[i][j]表示a的前i个字符 b的前j个字符的相同序列
int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++)cin>>a[i];
  for(int i=1;i<=m;i++)cin>>b[i];
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
        if(a[i]==b[j])dp[i][j]=dp[i-1][j-1]+1;
        else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    }
  }
  cout<<dp[n][m];
  return 0;
}

14.二进制中 1 的个数 - 蓝桥云课 (lanqiao.cn)

#include <bits/stdc++.h>
using namespace std;
int main(){
  unsigned int n;cin>>n;
  int ans=0;
  while(n){
    if(n&1==1)ans++;
    n>>=1;
  }
  cout<<ans;
  return 0;
}

0前缀判定 - 蓝桥云课 (lanqiao.cn)


//本题是一道字典树的模板题
//字典树是一种高效率存储多个字符串的数据结构
//其每个结点的权值代表以该结点结尾的字符串的数量,每条边存储一个字符
//从根结点开始,按某一路径遍历到某一结点,即得到一种字符串,其个数等于当前结点存储的数值
//如从根结点开始,依次走过'a''b''c'三条边到达9号结点,9号结点保存的数字是3
//则得到字符串"abc",其数量为3个 
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+100;
 
int nex[N][27];//nex[i][0]表示从结点i出发,边为'a'的下一个结点地址(假设字符串全由小写字母构成)
//如1号结点与2号结点间存在一条记录字母'a'的边,则nex[1]['a'-'a']=2 
//如8号结点与9号结点间存在一条记录字母'c'的边,则nex[8]['c'-'a']=9 
int cnt[N];//cnt[i]表示以结点i结尾的字符串的数量,即每个结点的权值 
int idx=2;//用于动态开点,初始时只有一个根结点1 
 
void insert(char *S)//在字典树中插入字符串S的信息 
{
    int x=1;//x表示结点编号,初始从根结点(1号)开始 
    for(int i=0;S[i]!='\0';i++)//遍历字符串S 
    {
        //先检查x是否存在S[i]的边 
        if(nex[x][S[i]-'a']==0)//从结点x出发,目前还没有记录当前字母的边 
        {
            nex[x][S[i]-'a']=idx++;//则新建一个边记录之,同时动态开点 
        }
        x=nex[x][S[i]-'a'];//到达下一个结点编号
    }    
    //cnt[x]++;
    //最终x到达字符串末尾字符对应的结点上,其计数值加1 
}
 
bool check(char *T)//在字典树中查找字符串T(计算出现的次数)
{
    int x=1;//x表示结点编号,初始从根结点(1号)开始 
    for(int i=0;T[i]!='\0';i++)//遍历字符串T 
    {
        x=nex[x][T[i]-'a'];//根据当前字符,x不断向下追溯,最终到达结尾
        //若不存在这个字符(记录这个字符的边),则x=0,后续x将一直为0 
    }    
    //return cnt[x];//返回字符串T出现的次数,即结尾字符对应的结点所记录的权值 
    return x;//本题返回x即可,只需判断x是否为0 
}
 
int main(){
    int n,m;
    cin>>n>>m;
    while(n--)//N个字符串 
    {
        char S[N];
        cin>>S;
        insert(S);//每输入一个字符串,就将其信息插入字典树 
    }    
    while(m--)//M个询问 
    {
        char T[N];
        cin>>T;
        if(check(T))cout<<"Y"<<endl;//在字典树中找到T,输出Y 
        else cout<<"N"<<endl;//没找到,输出N 
    }
    return 0;
}

0区间最大值 - 蓝桥云课 (lanqiao.cn) 

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+6;
int st[N][22];//st[i][j]表示从i开始2^j个数的区间最大值
int n,q;
int a[N];
int get_max(int l,int r){
  int k=log(r-l+1)/log(2);//长度2^k
  //右端点是r,长度为2^k时候  左端点是r-2^k+1
  return max(st[l][k],st[r-(1<<k)+1][k]);
}
int main(){
  cin>>n>>q;
  for(int i=1;i<=n;i++)cin>>a[i];
  for(int i=1;i<=n;i++)st[i][0]=a[i];
  
  for(int j=1;j<=20;j++){//枚举区间长度
    for(int i=1;i<=n;i++){//枚举左端点
        if(i+(1<<j)-1<=n){//右端点合法  //分出来的右端点的右边
            st[i][j]=max(st[i][j-1],st[(1<<(j-1))+i][j-1]);
        }//
    }
  }
  while(q--){
    int l,r;cin>>l>>r;
    cout<<get_max(l,r)<<'\n';
  }
  return 0;
}

0最长回文子串 - 蓝桥云课 (lanqiao.cn) 

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+7;//开2倍
char S[N],T[N];
int p[N];//表示回文半径
int main(){
  cin>>S+1;int n=strlen(S+1);
  for(int i=2*n+1;i>=1;i--){
    int t=i;
    T[t]=(t&1)?'#':S[t>>1];
  }//用一个字符串也可以
  T[0]='^',T[2*n+2]='$';
  int C=0,R=0;
  for(int i=1;i<=2*n+1;i++){//R表示当前已知的回文子串的最右边界位置
// 如果 i < R,说明当前位置 i 在当前已知的回文子串范围内。此时,R-i 表示当前位置 i 到已知回文子串右边界 R 的距离。
// 如果 R-i 的值更小,说明当前位置 i 距离已知回文子串右边界更近,因此可以直接利用该距离更新当前位置 i 处的回文半径
    p[i]=(i<R)?min(R-i,p[2*C-i]):1;//保证合法 右端点不超出R因为R右边不知道
// 且长度至少是1
    while(T[i+p[i]]==T[i-p[i]])p[i]++;//再前面的基础上 半径还可以更大
    if(i+p[i]>R){//下一个右边的顶点已经顶到'$'了
        C=i;//更换中心
        R=i+p[i];//更换右端点
    }
  }
  int ans=0;
  for(int i=1;i<=2*n+1;i++)ans=max(ans,p[i]-1);
  cout<<ans;
  return 0;
}

0石子合并 - 蓝桥云课 (lanqiao.cn)

//这是一道区间dp的模板题
//可以将一个大区间的问题拆分成若干个子区间合并的问题
//两个连续的子区间可以进行合并成一个大区间
//基本步骤为:
//1、从小到大枚举区间长度
//2、对每一个区间长度,枚举所有可能的区间(左右端点)
//3、对每一个区间,枚举所有可能的分割点,以分割点为界将其划分为两个子区间
//4、计算这两个子区间的合并代价
//5、通过比较所有的子区间组合,求出当前区间的最优值 
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=250;
int stone[maxn];
int dp[maxn][maxn];
//dp[i][j]表示将[i,j]的区间合并为一堆所需的最小代价
signed main(){
    int n;
    cin>>n;
    memset(dp,0x3f,sizeof(dp));//由于不断更新最小值,故dp数组初始化为极大值 
    for(int i=1;i<=n;i++)
    {
        cin>>stone[i];
        dp[i][i]=0;//初始化:对于单独的一堆石头,合并代价是0(无需合并) 
        stone[i]+=stone[i-1];//计算前缀和,便于后续计算合并代价 
    }    
    
    for(int len=2;len<=n;len++)//枚举所有可能的区间长度2~n 
    {
        for(int i=1;i+len-1<=n;i++)//枚举所有可能的左端点 
        {
            int j=i+len-1;//j为对应的右端点 
            for(int k=i;k<j;k++)//枚举区间[i,j]中的所有分割点k 
            {
                //k将区间分成[i,k]和[k+1,j]两部分
                //先将[i,k]合并为一堆,代价为dp[i][k];再将[k+1,j]合并为一堆,代价为dp[k+1][j] 
                //最后将这两堆合并为一堆,代价为cost,cost可用前缀和算出
                //合并[i,j]区间的代价为以上三者之和,即dp[i][j]=dp[i][k]+dp[k+1][j]+cost 
                //取最小花费更新dp[i][j]即可 
                int cost=stone[j]-stone[i-1];//计算将[i,j]的两堆石子合并的代价 
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+cost);
            }
        }
    }
    cout<<dp[1][n]<<'\n';//将[1,n]区间合并的最小代价即为最终答案 
    return 0;
}

唯一分解定理:任何一个>1的正整数 都能以一种唯一的方式分解为若干个质因子的乘积

约数个数定理:分解的质因子的次方分别+1相乘

约数和定理:从0次方加到对应次方的和 相乘

1.阶乘的约数和 - 蓝桥云课 (lanqiao.cn) 

 

//求n!的约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=2e5+100;
map<int,int>factor;//键存储质因数,值存储其出现的次数 

void cal_factor(int x)//计算x的质因数出现的次数 
{
    for(int i=2;i<=x/i;i++)//从2遍历到根号x 
    {
        if(x%i)continue;
        while(x%i==0)//i是x的质因数 
        {
            if(factor.find(i)!=factor.end())
            {
                factor[i]++;//出现次数加1 
            }
            else factor.insert(make_pair(i,1));
            x=x/i;    
        }    
    }    
    //经过以上处理,x可能剩下一个大于1的数,如x=12,将分解为2*2*3,最后将剩下3 
    if(x>1)factor[x]++;//特判,最后一个因子出现的次数加1 
}

ll qmi(ll a,ll b,int m ){
  ll res=1;
  while(b){
  if(b&1){res=(res*a+m)%m;b--;}//记得--
  a=a*a%m;//不断取模
  b>>=1;
  }
  return res%m;
}

int main(){
    int n;
    cin>>n;
    ll ans=1;
    map<int,int>::iterator it1;
    for(int i=1;i<=n;i++)cal_factor(i);
    
    for(it1=factor.begin();it1!=factor.end();it1++)//遍历所有出现的质因数 
    {
        ll base=it1->first;//取出当前的质因数 
        ll power=it1->second;//取出其出现的次数 
        ll tmp=0;//tmp为(1+pi^1+pi^2+...+pi^ki) 
        for(int i=0;i<=power;i++)
        {
            tmp=(tmp+qmi(base,i,mod)+mod)%mod;
        }
        ans=(ans*tmp+mod)%mod;
        //ans为 ∏(1+pi^1+pi^2+...+pi^ki)
    }
    cout<<ans<<endl;
    return 0;
}

 

#include <bits/stdc++.h>
#define int long long
using namespace std;
vector<pair<int,int> >v;
signed main(){
  int n;cin>>n;
  for(int i=2;i<=n/i;i++){
    if(n%i)continue;//不能整除直接跳过
    //如果可以整除那么一定是一个质因子
    int cnt=0;//表示当前这个质因子i的指数
    while(n%i==0){n/=i;cnt++;}
    v.push_back({i,cnt});
  }
  //可能有一个大于平方根的因子
  if(n>1)v.push_back({n,1});
  for(auto i:v)cout<<i.first<<" "<<i.second<<'\n';
  //2 2
  //3 1
  //5 1
  return 0;
}

13.最小正整数解 - 蓝桥云课 (lanqiao.cn) 

//裴蜀定理:设a,b是不全为0的整数,则存在整数x,y使得ax+by=k*gcd(a,b) 
//扩展裴蜀定理:
//a,b为不小于0的整数,n为整数,是否存在不小于0的x和y使得ax+by=n有解?
//1、若n>ab-a-b,有解
//2、若n=0,有解(x=y=0)
//3、若n<0,无解(a,b,x,y均不小于0,无法线性变换出负数)
//4、若ax+by=n有解,则ax+by=ab-a-b-n无解

//本题需要求解最小的ax+by=n,使得n>0
//设a和b的最大公约数为gcd(a,b),因为a,b,x,y均为整数,其线性组合同样是gcd(a,b)的倍数
//故ax+by=k*gcd(a,b) 
//令k=1,可得最小的ax+by=gcd(a,b) 
//故本题直接求解gcd(a,b)即可
//注意当ab异号时gcd(a,b)返回负数,需要取绝对值 
#include <bits/stdc++.h>
using namespace std;
int main(){
    int T;
    cin>>T;
    while(T--){
        int a,b;
        cin>>a>>b;
        cout<<abs(__gcd(a,b))<<'\n';//ab异号将返回负数,需要取绝对值 
    }
    return 0;
}

 4|12==7|21

 

 

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值