牛客小白月赛22

牛客小白月赛22


B.树上子链

题意:

给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和 。
请在树 T 中找出一条最大的子链并输出。

思路:

类似树形dp求树的直径。
d(i)表示以点i到以i为根的子树任意一点的最大距离
dfs过程中我们记录子树中的最大和次大。
1.如果最长链不经过父节点那条边,那么最大和次大连接就是经过当前点的最长链。
2.如果最长链经过父节点那条边,那么在回溯到父节点那层时就能计算出正确的最长链。
所以不考虑父节点并不失完备性。

对经过每个点的最长链取max就是本题答案

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
vector<int>g[maxm];
int a[maxm];
int d[maxm];
int ans=-1e18;//因为有负点权,所以ans要初始化为-inf
void dfs(int x,int fa){
    int t1=0,t2=0;//t1,t2记录最大和次大
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x);
        if(d[v]>t1){
            t2=t1;
            t1=d[v];
        }else if(d[v]>t2){
            t2=d[v];
        }
    }
    d[x]=t1+a[x];
    ans=max(ans,t1+t2+a[x]);
}
signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,-1);
    cout<<ans<<endl;
    return 0;
}

C.交换游戏

题意:

给一个长度为12的只含o和-的串
一种操作是将串中的子串oo-变成–o
一种操作是将串中的子串-oo变成o–
问无限次操作之后串中的o最少为多少

思路:

每次操作之后会得到一个新的序列,接下来就是要计算新序列的最小值。
因此可以发现原串的最小值就是其一步操作所能得到的所有新串的最小值。
一步操作得到的新串的最小值又是新新串中的最小值…因此记忆化搜索取min即可。
因为串的长度只有12,所以可以状压为二进制串,方便操作。

code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1<<12;
bool mark[maxm];
int ans[maxm];
void solve(int x){
    if(mark[x])return ;
    mark[x]=1;
    for(int j=0;j<12;j++)ans[x]+=(x>>j&1);//初始化答案为1的数量
    for(int k=2;k<12;k++){
        int i=k-2,j=k-1;
        if((x>>i&1)&&(x>>j&1)&&!(x>>k&1)){//oo-
            int temp=x-(1<<i)-(1<<j)+(1<<k);
            solve(temp);
            ans[x]=min(ans[x],ans[temp]);
        }else if(!(x>>i&1)&&(x>>j&1)&&(x>>k&1)){//-oo
            int temp=x+(1<<i)-(1<<j)-(1<<k);
            solve(temp);
            ans[x]=min(ans[x],ans[temp]);
        }
    }
}
signed main(){
    for(int i=0;i<(1<<12);i++)solve(i);//直接预处理所有情况
    int T;
    cin>>T;
    while(T--){
        string s;
        cin>>s;
        int x=0;
        for(int j=0;j<12;j++){
            if(s[j]=='o'){
                x|=(1<<j);
            }
        }
        cout<<ans[x]<<endl;
    }
    return 0;
}

H.货物种类

题意:

给n和m表示n个谷仓m种操作
每种操作(L,R,d),表示吧区间[L,R]这段的谷仓都加入货物d
问m次操作之后那个谷仓的货物最多
n<=1e5,m<=1e5,d<=1e9

思路:

差分,和单纯的数加减法的差分差不多。
区别仅在于每个位置可能添加或者减少多个数,不能和加减法一样直接操作。
需要开两个数组存下每个位置要添加或者减少的数。
详见代码。

code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
vector<int>add[maxm];//添加
vector<int>del[maxm];//减少
map<int,int>mark;//记录每个数出现次数
signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int l,r,d;
        cin>>l>>r>>d;
        add[l].push_back(d);
        del[r+1].push_back(d);
    }
    int ans=0;
    int ma=0;
    int now=0;
    for(int i=1;i<=n;i++){
        for(int v:add[i]){
            mark[v]++;
            if(mark[v]==1)now++;
        }
        for(int v:del[i]){
            mark[v]--;
            if(mark[v]==0)now--;
        }
        if(now>ma){
            ma=now;
            ans=i;
        }
    }
    cout<<ans<<endl;
    return 0;
}

I.工具人(skiped)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值