【寒假集训营总结笔记——7道优质好题】

牛客寒假集训营总结笔记——7道优质好题


一、Trie树的应用:

题目链接:Tokitsukaze and Min-Max XOR

1、题意

在这里插入图片描述


2、题解

1、首先这道题的答案和元素本身的顺序是无关的,因为假如你选择了一些数字,它是默认必须排好序才能记作是答案,所以对于我们枚举的任意一组max和min,位于 m i n ≤ x ≤ m a x min \le x \le max minxmax的x是可取可不取的,所以就是 2 i − j − 1 + 1 2^{i-j-1}+1 2ij1+1就是当前位置作为max的贡献,怎么快速得到min呢,我们可以用01trie树得到。

2、01trie树详解(具体看代码):
首先是建树了,在每个节点的周围都放一个val节点存放该节点的儿子节点所有的 ∑ ( 2 − i ) \sum(2^{-i}) (2i)
然后就是搜索了,我们贪心的来使用trie树,当k在我们枚举的二进制位置为0时,当前二进制位必须和当前的max相抵消才能合法,当我们到达k的二进制为1的位置时,我们就可以直接把1^(max当前的二进制位)的贡献直接加上了,然后继续往下,类似于数位dp一样的思想去看。


3、代码

#include<bits/stdc++.h>
#define int long long 
#define endl '\n' 
using namespace std; 
const int N = 2e5 + 10;
const int inf = 0x3f3f3f3f; 
const int mod = 1e9 + 7; 
int n,m,cnt;
int a[N];
int son[N*40][2], val[N*40];

int qpow(int x, int y) {
    int res = 1; 
    while(y) {
        if(y & 1) res = res * x % mod;  
        x = x * x % mod; 
        y >>= 1; 
    }
    return res; 
}
// 建树
void insert(int x, int nums) {
    int p = 1; 
    for(int i = 35; i >= 0; -- i ) {
        int t = x >> i & 1; 
        if(!son[p][t]) {
            son[p][t] = ++ cnt;
            int q = cnt;
            son[q][1] = son[q][0] = 0;
            val[q]=0;
        }
        
        p = son[p][t]; 
        val[p] = (val[p]+nums)%mod;
    }
}
//查询操作
int query(int x, int k) {
    int res=0,p=1;
    for(int i = 35; i >= 0; -- i ) {
        int ps = k >> i & 1;
        int cur = x >> i & 1; 
        if(ps) {
            if(son[p][cur]) res = (res + val[son[p][cur]]) % mod;
            p = son[p][1^cur];
        }
        else p = son[p][cur];
    }
    res = (res + val[p]) % mod;
    return res; 
}
void solve() {
    cin >> n >> m;
    cnt=1;
    for(int i = 1; i <= n; ++ i ) cin >> a[i]; 
    sort(a + 1, a + 1 + n);
    val[cnt]=0;
    son[cnt][0]=son[cnt][1]=0;
    int ans=0;
    for(int i = 1; i <= n; ++ i ) {
        ans = (ans + 1 + qpow(2,i - 1) * query(a[i],m) % mod) % mod;
        
        insert(a[i],qpow(qpow(2,i),mod-2));
    }
    cout<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout.tie(0); 
    int T; 
    cin >> T; 
    while(T -- ) solve();
    
    return 0; 
}


二、朴素懒标记的应用和模板总结:

题目链接: Tokitsukaze and Power Battle (easy)

1、题意:

在这里插入图片描述


2、题解

数据范围已经告诉我们了,只会增加非负数并且原数组元素也都是非负数,因为要让差距尽可能的大,所以肯定要从左边一直选择并且另一个玩家选择右边的后一个,保证它是最小的,比如:给定了我们 l , r l,r l,r,那我们需要的方案肯定是 m a x ( s u m [ L . . R ] − a [ R + 1 ] ) max(sum[L..R] - a[R + 1]) max(sum[L..R]a[R+1]),因为我们贪心可以知道必须选择第一个元素,那么其实我们只需要维护一个 s [ 1.... l ] − a [ l + 1 ] s[1....l] - a[l + 1] s[1....l]a[l+1],加上懒标记即可,比如我们需要讲将 a x a_x ax改成 y y y,只需要维护区间加法即可,即区间 x . . . n x...n x...n加上 − a x + y {-a_x+y} ax+y,与此同时我们再维护一个树状数组动态记录数组的前缀和,这样查询的时候我们就可以总结 q u e r y ( 1 , l , r ) − s u m ( l − 1 ) query(1,l,r)-sum(l-1) query(1,l,r)sum(l1)

3、懒标记维护区间加法和区间最大值最小值:

#define int long long 
using namespace std; 
const int N = 5e5 + 10; 
const int inf = 0x3f3f3f3f; 
int n, m; 
int a[N]; 
struct node 
{
    int l, r; 
    int mx,mn;
    int lz; 
}tr[N * 4]; 
 
 
void pushup(node &t, node& l, node& r)  
{
    t.mx = max(l.mx, r.mx);
    t.mn = min(l.mn, r.mn); 
}
void pushup(int u) 
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]); 
}
 
 
void pushdown(int u) 
{
    if(!tr[u].lz) return; 
    node &l = tr[u << 1], &r = tr[u << 1 | 1]; 
    l.mx = l.mx + tr[u].lz, l.lz += tr[u].lz; 
    r.mx = r.mx + tr[u].lz, r.lz += tr[u].lz; 
    l.mn = l.mn + tr[u].lz; 
    r.mn = r.mn + tr[u].lz; 
    tr[u].lz = 0; 
    pushup(u); 
}
 
void build(int u, int l, int r) 
{
    tr[u].l = l, tr[u].r = r; 
    if(l == r)
    {
        tr[u].lz = 0; 
        tr[u].mx = a[l] - (a[l + 1] - a[l]); 
        tr[u].mn = a[l] - (a[l + 1] - a[l]); 
    }
    else
    {
        tr[u].lz = 0; 
        int mid = l + r >> 1; 
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); 
        pushup(u); 
    }
}
 
void change(int u, int l, int r, int d)  
{
    node& t = tr[u]; 
    if(t.l >= l && t.r <= r) 
    {
        t.mx += d; 
        t.mn += d; 
        t.lz += d; 
    }
    else
    {
        pushdown(u); 
        int mid = t.l + t.r >> 1; 
        if(l <= mid) change(u << 1, l, r, d); 
        if(r > mid) change(u << 1 | 1, l, r, d); 
        pushup(u); 
    }
}
int query_mx(int u, int l, int r) 
{
    node t = tr[u]; 
    if(t.l >= l && t.r <= r) return t.mx; 
    else
    {
        pushdown(u); 
        int res = -1e18; 
        int mid = t.l + t.r >> 1; 
        if(l <= mid)  res = max(res,query_mx(u << 1, l, r)); 
        if(r > mid) res = max(res,query_mx(u << 1 | 1, l, r)); 
        return res; 
    }
}
 
int query_mn(int u, int l, int r) 
{
    node t = tr[u]; 
    if(t.l >= l && t.r <= r) return t.mn; 
    else
    {
        pushdown(u); 
        int res = 1e18; 
        int mid = t.l + t.r >> 1; 
        if(l <= mid)  res = min(res,query_mn(u << 1, l, r)); 
        if(r > mid) res = min(res,query_mn(u << 1 | 1, l, r)); 
        return res; 
    }
}

4、代码:

#include <iostream> 
#include <algorithm> 
#include <cstdio> 
#include <cstring> 

#define int long long 
using namespace std; 
const int N = 5e5 + 10; 
const int inf = 0x3f3f3f3f; 
int n, m; 
int a[N]; 
struct node 
{
    int l, r; 
    int mx,mn;
    int lz; 
}tr[N * 4]; 


void pushup(node &t, node& l, node& r)  
{
    t.mx = max(l.mx, r.mx);
    t.mn = min(l.mn, r.mn); 
}
void pushup(int u) 
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]); 
}


void pushdown(int u) 
{
    if(!tr[u].lz) return; 
    node &l = tr[u << 1], &r = tr[u << 1 | 1]; 
    l.mx = l.mx + tr[u].lz, l.lz += tr[u].lz; 
    r.mx = r.mx + tr[u].lz, r.lz += tr[u].lz; 
    l.mn = l.mn + tr[u].lz; 
    r.mn = r.mn + tr[u].lz; 
    tr[u].lz = 0; 
    pushup(u); 
}

void build(int u, int l, int r) 
{
    tr[u].l = l, tr[u].r = r; 
    if(l == r)
    {
        tr[u].lz = 0; 
        tr[u].mx = a[l] - (a[l + 1] - a[l]); 
        tr[u].mn = a[l] - (a[l + 1] - a[l]); 
    }
    else 
    {
        tr[u].lz = 0; 
        int mid = l + r >> 1; 
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); 
        pushup(u); 
    }
}

void change(int u, int l, int r, int d)  
{
    node& t = tr[u]; 
    if(t.l >= l && t.r <= r) 
    {
        t.mx += d; 
        t.mn += d; 
        t.lz += d; 
    }
    else 
    {
        pushdown(u); 
        int mid = t.l + t.r >> 1; 
        if(l <= mid) change(u << 1, l, r, d); 
        if(r > mid) change(u << 1 | 1, l, r, d); 
        pushup(u); 
    }
}
int query_mx(int u, int l, int r) 
{
    node t = tr[u]; 
    if(t.l >= l && t.r <= r) return t.mx; 
    else 
    {
        pushdown(u); 
        int res = -1e18; 
        int mid = t.l + t.r >> 1; 
        if(l <= mid)  res = max(res,query_mx(u << 1, l, r)); 
        if(r > mid) res = max(res,query_mx(u << 1 | 1, l, r)); 
        return res; 
    }
}

int query_mn(int u, int l, int r) 
{
    node t = tr[u]; 
    if(t.l >= l && t.r <= r) return t.mn; 
    else 
    {
        pushdown(u); 
        int res = 1e18; 
        int mid = t.l + t.r >> 1; 
        if(l <= mid)  res = min(res,query_mn(u << 1, l, r)); 
        if(r > mid) res = min(res,query_mn(u << 1 | 1, l, r)); 
        return res; 
    }
}
int trr[N]; 

#define lowbit(x) (x&-x) 
void add(int x, int c) {
    for(int i = x; i <= n; i += lowbit(i)) trr[i] += c; 
}
int sum(int x) {
    int res = 0; 
    for(int i = x; i; i -= lowbit(i)) res += trr[i]; 
    return res; 
}
void solve() {
    scanf("%lld%lld",&n,&m); 
    for(int i = 0; i <= n; ++ i ) trr[i] = 0; 
    for(int i = 1; i <= n; ++ i ) {
        scanf("%lld",&a[i]); 
        add(i,a[i]);
        a[i] += a[i - 1]; 
    }
    a[n + 1] = a[n]; 
    build(1,1,n);
    while(m -- ) {
        int x, y, z; 
        cin >> x >> y >> z; 
        if(x == 1) {
            int tt = sum(y) - sum(y-1);
            add(y,z-tt);
            change(1,y,n,z-tt);
            if(y-1>=1) change(1,y-1,y-1,-(z-tt));
        }
        else {
            cout<<query_mx(1,y,z-1)-sum(y-1)<<endl;
        }
    }
}
signed main() 
{
    int ts; 
    
    scanf("%lld",&ts);
    
    while(ts -- ) solve(); 
    
    return 0; 
}

三、第二类斯特林数

题目链接:鸡数题


1、题意:

在这里插入图片描述


2、题解:

问题可以很容易的转换成将n个小球放到m个盒子的方案,并且每个盒子都要有小球,第二类斯特林数是一个组合数学中的概念,表示将 n n n 个不同的物体划分成 k k k 个非空的不可辨别的集合的方法数。

通项公式为:转自oi-wiki
在这里插入图片描述


3、代码

#include <bits/stdc++.h> 
#define int long long 
using namespace std; 
const int N = 2e5 + 10; 
const int mod = 1e9 + 7; 
int n, m; 
int f1[N], f2[N]; 
int qmi(int x, int y) {
    int res = 1; 
    while(y) {
        if(y & 1) res = res * x % mod; 
        x = x * x % mod; 
        y >>= 1; 
    }
    return res; 
}
int f(int a, int b) {
    return f1[a] * f2[a - b] % mod * f2[b] % mod; 
}
signed main() {
    cin >> n >> m; 
    if(n < m) {
        cout << 0 << endl; 
        return 0; 
    }
    if(n == m) {
        cout << 1 << endl; 
        return 0; 
    }
    f1[0] = 1, f2[0] = 1;  
    
    for(int i = 1; i <= n; ++ i ) {
        f1[i] = (f1[i - 1] * i) % mod;
        f2[i] = qmi(f1[i], mod - 2);  
    }
    
    int ans = 0; 
    for(int i = 0; i <= m; ++ i )
        ans = (ans + qmi(-1, i) * f(m, i) % mod * qmi(m - i, n) % mod) % mod;
    ans = (ans * f2[m] % mod + mod) % mod;
    
    cout<<ans<<endl;
    return 0; 
}


四、贡献题的新思路

题目链接:tomorin的字符串迷茫值


1、题意:

在这里插入图片描述


2、题解:

一般我们做这种贡献题都是想着每种方案对答案的贡献,这道题换了一种思考的角度,我们对每种情况内的每个子情况对子情况所在的所有情况的贡献,比如mygo,就会有mygo, m_ygo,m_y_go…,m和y,y和g,g和o之间的位置都可以选和不选,求每个位置如果有当前这种情况,两边任意删除的情况就是它对他所在方案内的贡献。

怎么计算两边的任意情况的贡献呢,这里就需要dp了,dp[i]定义为长度为i的字符串,不删除连续位置的所有方案数。那么我们只需要考虑最后一个位置的字符,它既可以删也可以不删,如果保留就是dp[i-1],,如果删除,那就是dp[i-2],因为i-1不能删除,然后就是 d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i] = dp[i-1]+dp[i-2] dp[i]=dp[i1]+dp[i2],就是斐波那契数列。

对于任意位置i…j的贡献计算就是: d p [ i − 1 ] ∗ d p [ n − i ] dp[i-1]*dp[n-i] dp[i1]dp[ni]


3、代码

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N = 2e5 + 10; 
const int mod = 1e9 + 7; 
int n,m,k;
int f[N]; 
int qpow(int x, int y) {
    int res = 1; 
    while(y) {
        if(y&1)res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res; 
}

signed main() {
    f[0]=1,f[1]=2;
    for(int i = 2; i < N; ++ i ) f[i] = (f[i - 1] + f[i - 2]) % mod; 
    string s;
    cin>>s;
    n=s.size();
    int ans=0;
    for(int i = 0; i < 1<<3; ++ i ) {
        string us="m";
        if(i>>0&1)us+='1';
        us+='y';
        if(i>>1&1)us+='1';
        us+='g';
        if(i>>2&1)us+='1';
        us+='o';
        int le=us.size();
        int t = 0;
        for(int j = 0; j + le - 1 < n; ++ j ) {
            string tmp = s.substr(j,le);
            bool flag=1;
            for(int k = 0; k < le; ++ k )
                if(us[k]!=tmp[k]&&us[k]!='1') {
                    flag=0;
                    break; 
                }
            if(flag) {
                ans = (ans + f[j]*f[n-1-(j+le-1)]%mod)%mod;
            }
        }
    }
    cout<<ans<<endl;
    return 0; 
}

五、完全背包变形题

题目链接:soyorin的通知

完全背包板子:

#include<bits/stdc++.h> 
#define int long long 
using namespace std; 
const int N = 1010 + 10;
int n,m;
int dp[N];
signed main() {
    cin>>n>>m;
    // memset(dp,-0x3f,sizeof dp);
    dp[0]=0;
    for(int i = 1; i <= n; ++ i ) {
        int v,w;
        cin>>v>>w;
        for(int i = v; i <= m; ++ i ) 
            dp[i]=max(dp[i],dp[i-v]+w);
    }
    cout<<dp[m]<<endl;
    
    return 0;
}

1、题意

在这里插入图片描述


2、题解

因为刚开始必须要至少先用p的代价先通知一个人,才能进行完全背包,那我们不妨先进行完全背包,然后再枚举用了几次的p,但注意必须至少一次。


3、代码

#include<bits/stdc++.h>
#define int long long 
using namespace std; 
const int N = 1010;
int n,m;
int dp[N];
int a[N],b[N];
signed main() {
    cin>>n>>m;
    for(int i = 1; i <= n; ++ i ) cin>>a[i]>>b[i];
    
    memset(dp,0x3f,sizeof dp);
    dp[0]=0;
    for(int i = 1; i <= n; ++ i ) {
        for(int j = 0; j <= n; ++ j ) 
            dp[j]=min(dp[j],dp[max(0ll,j-b[i])]+a[i]);
    }
    int ans=1e18;
    for(int i = 0; i < n; ++ i )
        ans=min(ans,dp[i]+(n-i)*m);
//     cout<<dp[n]<<endl;
    cout<<ans<<endl;
    return 0; 
}



六、区间贪心问题 陡峭值问题

题目链接: rikki的数组陡峭值


1、题意

在这里插入图片描述


2、题解

首先假如我们有一堆的区间 [ l 1 , r 1 ] , [ l 2 , r 2 ] , . . . , [ l n , r n ] [l_1,r_1],[l_2,r_2],...,[l_n,r_n] [l1,r1],[l2,r2],...,[ln,rn], 此时我们假设有三个区间, s e g 1 = [ l 1 , r 1 ] , s e g 2 = [ l 2 , r 2 ] , s e g 3 = [ l 3 , r 3 ] seg_1=[l_1,r_1],seg_2=[l_2,r_2],seg_3=[l_3,r_3] seg1=[l1,r1],seg2=[l2,r2],seg3=[l3,r3]
对于 s e g 1 ∩ s e g 3 = ∅ , s e g 2 ∩ s e g 3 ≠ ∅ , s e g 2 ∩ s e g 1 ≠ ∅ seg_1 \cap seg_3 = \empty, seg_2 \cap seg_3 \neq \empty, seg_2 \cap seg_1 \neq \empty seg1seg3=,seg2seg3=,seg2seg1= s e g 2 seg_2 seg2 无论是和 s e g 1 seg_1 seg1或者是 s e g 3 seg_3 seg3合并, 结果一定 ≥ a b s ( s e g 3 . l − s e g 1 . r ) \geq abs(seg_3.l-seg_1.r) abs(seg3.lseg1.r),所以我们肯定优先合并 s e g 1 seg_1 seg1,因为合并 s e g 3 seg_3 seg3会使得后面能合并的区间变少,肯定更劣。
其它情况,很显然是和能合并的区间合并。


3、代码

#include <bits/stdc++.h>
#define lowbit(x) (x&-x)
#define int long long 
#define ff first 
#define ss second 
using namespace std;
using PII = pair<int, int>; 
const int N = 3e5 + 10;
const int inf = 1e18; 
int n, m, k; 
int dp[N][2];

void solve() {
    cin>>n;
    int l=1,r=1e9;
    vector<PII> ve;
    for(int i = 1; i <= n; ++ i ) {
        int L,R;
        cin>>L>>R;
        if(i==1) l=L,r=R;
        else {
            if(R<l||L>r) {
                ve.push_back({l,r});
                l=L,r=R;
            }
            else {
                l=max(l,L),r=min(r,R);
                // cout<<l<<' '<<r<<endl;
            }
        }
    }
    ve.push_back({l,r});
    // for(int i = 0; i < ve.size(); ++ i ) cout<<ve[i].ff<<" "<<ve[i].ss<<endl;
    if(ve.size()==1)cout<<0<<endl;
    else {
        for(int i = 1; i < ve.size(); ++ i ) {
            dp[i+1][0]=min(dp[i][0]+abs(ve[i].ff-ve[i-1].ff),dp[i][1]+abs(ve[i].ff-ve[i-1].ss)); 
            dp[i+1][1]=min(dp[i][0]+abs(ve[i].ss-ve[i-1].ff),dp[i][1]+abs(ve[i].ss-ve[i-1].ss));
        } 
        cout<<min(dp[ve.size()][0],dp[ve.size()][1])<<endl;
    }
}
signed main() {
    int ts=1;
    // cin >> ts; 
    while(ts -- ) solve();
    return 0; 
}

七、分部dp解决问题,将一个复杂难以解决的问题拆解。

题目链接:来点每日一题


1、题意

在这里插入图片描述


2、题解

一次性考虑所有的情况时间复杂度要做到 O ( n 3 ) O(n^3) O(n3),那肯定是超时了,我们不妨将问题转化成小的问题,先只考虑每段只选6个的方案,即 m x [ i ] [ j ] mx[i][j] mx[i][j],为i道j中选6个数的最优解,再做一遍线性dp,每次把一段看成一个整体, d p [ i ] = m a x ( d p [ i ] , d p [ j − 1 ] + m x [ j ] [ i ] ) dp[i] = max(dp[i], dp[j - 1] + mx[j][i]) dp[i]=max(dp[i],dp[j1]+mx[j][i]),然后求mx[i][j]的过程也是一个很经典我问题,就是从i到j中选6个数使得它满足题目中的式子最大的方案,因为其中带有负号,所以我们得同时求最小值。

我们分部来看,首先是求 m x [ i ] [ j ] mx[i][j] mx[i][j]的过程:

    for(int i = 1; i <= n; ++ i ) {
        int f[7][3]; 
        for(int j = 0; j <= 6; ++ j ) 
            f[j][0]=-1e9,f[j][1]=1e9;
        f[0][0]=f[0][1]=0;
        for(int j = i; j <= n; ++ j ) {    
            for(int k = 6; k >= 1; -- k )  // 从后往前滚动数组,因为要利用上一个状态更新,不能从小到大,类似降维背包的写法。
                for(int t = 0; t <= 1; ++ t)     
                {
                    if(abs(f[k-1][t])==1e9)continue; //到不合法不存在的方案要跳过,因为有负号可能会直接更新。
                    if(k % 2 == 0) {
                        f[k][0]=max(f[k][0],f[k-1][t]-a[j]);
                        f[k][1]=min(f[k][1],f[k-1][t]-a[j]);
                    }
                    else if(k == 1) {
                        f[k][0]=max(f[k][0],f[k-1][t]+a[j]);
                        f[k][1]=min(f[k][1],f[k-1][t]+a[j]);
                    }
                    else {
                        f[k][0]=max(f[k][0],f[k-1][t]*a[j]);
                        f[k][1]=min(f[k][1],f[k-1][t]*a[j]);
                    }
                }
            mx[i][j]=max(mx[i][j], f[6][0]);
        }
    }

有了 m x [ i ] [ j ] mx[i][j] mx[i][j]一切就很简单了


3、代码

#include<bits/stdc++.h>
#define int long long 
using namespace std; 
const int N = 1010; 
const int inf = 0x3f3f3f3f; 
int n,m;
int a[N];
int dp[N],mx[N][N]; 

signed main() {
    cin>>n;
    for(int i = 1; i <= n; ++ i ) cin>>a[i]; 
    
    for(int i = 1; i <= n; ++ i ) {
        int f[7][3]; 
        for(int j = 0; j <= 6; ++ j ) 
            f[j][0]=-1e9,f[j][1]=1e9;
        f[0][0]=f[0][1]=0;
        for(int j = i; j <= n; ++ j ) {
            for(int k = 6; k >= 1; -- k ) 
                for(int t = 0; t <= 1; ++ t)     
                {
                    if(abs(f[k-1][t])==1e9)continue; 
                    if(k % 2 == 0) {
                        f[k][0]=max(f[k][0],f[k-1][t]-a[j]);
                        f[k][1]=min(f[k][1],f[k-1][t]-a[j]);
                    }
                    else if(k == 1) {
                        f[k][0]=max(f[k][0],f[k-1][t]+a[j]);
                        f[k][1]=min(f[k][1],f[k-1][t]+a[j]);
                    }
                    else {
                        f[k][0]=max(f[k][0],f[k-1][t]*a[j]);
                        f[k][1]=min(f[k][1],f[k-1][t]*a[j]);
                    }
                }
            mx[i][j]=max(mx[i][j], f[6][0]);
        }
    }
    int ans=0;
    for(int i = 1; i <= n; ++ i ) {
        dp[i]=dp[i-1];
        for(int j = 1; j <= i; ++ j ) 
            dp[i] = max(dp[i], dp[j-1]+mx[j][i]),ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/535387.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

瑞芯微RK3568/RK3588+鸿蒙,矿鸿工控屏、矿鸿工控板、矿鸿网关,推动矿业数智化变革

4月10日至12日&#xff0c;以“绿色智能创新&#xff0c;携手共赢未来”为主题的第二届中国国际矿业装备与技术展览会在西安举行。信迈科技携矿鸿解决方案及产品亮相&#xff0c;赋能矿山行业数智化升级和国产化改造进程全面提速。 作为华为矿山军团矿鸿生态使能合作伙伴&#…

【LeetCode刷题笔记】LeetCode 1365.有多少小于当前数字的数字

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

Java8关于Function接口

Java学习-Function接口 1 函数式接口简介和学习地址2 两种常见的函数式接口2.1 Runnable&#xff1a;执行接口&#xff0c;不接收参数&#xff0c;也无返回结果。2.2 Consumer&#xff1a;作为消费接口&#xff0c;接收一个参数&#xff0c;无返回结果。 3 初识3.1 定义Functio…

渗透入门靶场大盘点

写给新手朋友入门&#xff0c;有了靶场丰富自己思路&#xff0c;也巩固自己的技术。当然新手老手都可以玩玩。 这期盘点渗透靶场&#xff0c;排名不分前后&#xff0c;还有其他靶场欢迎留言提出&#xff01;以及在留言当中评论出你最喜欢的靶场并附上理由。 本期是盘点入门必刷…

Java中学习Lambda表达式和stream中parallel并行方法

Java中学习Lambda表达式和stream中parallel并行方法 一、介绍二、整体代码 一、介绍 Lambda表达式是Java中一种方便的函数式编程风格&#xff0c;可以在集合操作中大大简化代码。而parallel()方法则是Java 8中引入的并发处理方法&#xff0c;可以让集合操作更快速高效。下面我…

qt进阶2:windows下可执行程序崩溃生成dmp,定位崩溃问题。

系列文章目录 文章目录 系列文章目录前言一、dmp文件生成二、使用步骤1.代码案例2.运行截图 前言 qt编译的可执行程序在windows下崩溃可生成dmp文件&#xff0c;用于调试定位崩溃原因。 一、dmp文件生成 略 二、使用步骤 1.代码案例 代码如下&#xff08;示例&#xff09;&…

Doodle Jump — 使用FlutterFlame开发游戏真不错!

前言 最近网上冲浪的时候&#xff0c;我偶然发现了一个国外的游戏网站&#xff0c;里面聚集了各种有趣的小游戏&#xff0c;类似于国内的4399。在浏览时&#xff0c;我遇到了一款经典的小游戏&#xff1a;Doodle Jump。上一次玩还是在上小学的时候&#xff0c;那时候父母在厨房…

2024.4.11

1.思维导图 2.指针形式验证大小端存储 #include<myhead.h>int main(int argc, const char *argv[]) {int num 0x12345678;char* ptr (char *)&num;if(*ptr 0x12){printf("big endian\n");}else if(*ptr 0x78){printf("little endian\n");}r…

LangChain-10 Agents langchainhub 共享的提示词Prompt

LangChainHub 的思路真的很好&#xff0c;通过Hub的方式将Prompt 共享起来&#xff0c;大家可以通过很方便的手段&#xff0c;短短的几行代码就可以使用共享的Prompt。 我个人非常看好这个项目。 官方推荐使用LangChainHub&#xff0c;但是它在GitHub已经一年没有更新了&#x…

Linux函数学习 epoll

1、Linux epoll函数 1.1、创建epoll实例 int epoll_create1(int flag); 返回值&#xff1a;-1 失败&#xff0c;非负数 成功 flag &#xff1a;默认传入0 1.2、管理epoll对象 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epfd &#xff1a;e…

.a和.so库文件是什么?

我们在编译开源代码后&#xff0c;通常会生成.a和.so这两个库文件&#xff0c;这两个文件有什么区别&#xff1f;又如何使用&#xff1f; 在 Linux 中&#xff0c;.a 和 .so 文件都是库文件&#xff0c;但它们有一些区别&#xff1a; 静态库文件&#xff08;.a&#xff09;&am…

PaddleDetection 项目使用说明

PaddleDetection 项目使用说明 PaddleDetection 项目使用说明数据集处理相关模块环境搭建 PaddleDetection 项目使用说明 https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.7/configs/ppyoloe/README_cn.md 自己项目&#xff1a; https://download.csdn.net/d…

融中财经专访 | 欧科云链:从跟随行业到引领行业

导读 THECAPITAL 新行业中的经验“老兵”。 本文4089字&#xff0c;约5.8分钟 作者 | 吕敬之 编辑 | 吾人 来源 | 融中财经 &#xff08;ID&#xff1a;thecapital&#xff09; 一个新兴行业从发展到成熟需要几个必要的推手&#xff1a;人才、产品、制度。 Web3.0&…

每天五分钟深度学习:逻辑回归算法的损失函数和代价函数是什么?

本文重点 前面已经学习了逻辑回归的假设函数,训练出模型的关键就是学习出参数w和b,要想学习出这两个参数,此时需要最小化逻辑回归的代价函数才可以训练出w和b。那么本节课我们将学习逻辑回归算法的代价函数是什么? 为什么不能平方差损失函数 线性回归的代价函数我们使用…

Hystrix:实现分布式系统的延迟处理和容错保护机制

文章目录 一、Hystrix的概念与作用1.1、资源隔离1.2、熔断器模式1.3、命令模式1.4、监控和报警 二、Hystrix的使用方法三、总结 一、Hystrix的概念与作用 Hystrix是Netflix开源的一个库&#xff0c;用于处理分布式系统中的延迟和容错。它通过在服务调用之间添加保护机制&#…

Leetcode刷题-字符串详细总结(Java)

字符串 字符串可能在算法处理上面和数组是类似的&#xff0c;但是String和数组的数据结构还是有一些不一样的 1、反转字符串 344. 反转字符串 - 力扣&#xff08;LeetCode&#xff09; 双指针的经典应用&#xff0c;两个指针同时向中间移动 public void reverseString(char[…

VMware启动显示“打开虚拟机时出错: 获取该虚拟机的所有权失败”

提示框&#xff08;忘截图了&#xff09;里提示目录C:\Users\mosep\Documents\Virtual Machines\VM-Win10 x64\中的某个文件&#xff08;在我这里好像是VM-Win10 x64.vmx&#xff0c;VM-Win10 x64是我给虚拟机取的名字&#xff09;在被使用中。 找到这个目录&#xff0c;删除.…

Python+Selenium+Unittest 之Unittest4(断言)

在unittest框架的TestCase类也提供了多种断言的方法。 断言常用方法 断言方法检查内容assertEqual(a,b)判断a是否等于b&#xff08;判断两个是不是同一个值&#xff09;assertNotEqual(a, b)判断a是否不等于b&#xff08;判断两个是不是同一个值&#xff09;assertTrue(a)判断a…

RAG应用开发实战(01)-RAG应用框架和解析器

1 开源解析和拆分文档 第三方的工具去对文件解析拆分&#xff0c;去将我们的文件内容给提取出来&#xff0c;并将我们的文档内容去拆分成一个小的chunk。常见的PDF word mark down, JSON、HTML。都可以有很好的一些模块去把这些文件去进行一个东西去提取。 优势 支持丰富的文…

[RK3399 Linux] 移植Linux 5.2.8内核详解

背景是在RK3399上面移植Rockchip官方提供的u-boot 2017.09 一、linux内核 1.1 源码下载 内核源码下载地址为:《https://www.kernel.org/》: 也可以到内核镜像网址下载https://mirrors.edge.kernel.org/pub/linux/kernel/,这里下载速度更快。 如果下载速度太慢,无法下载,…