当前位置: 首页 > news >正文

传智杯-省赛-第二场(B组)题解

小苯点兵点将

思路:签到题,从lr遍历判断可以;也可以求出 [1,l-1] 中 3 倍数的个数,和 [1,𝑟]中 3 倍数的个数,作差判断结果是否大于等于 1

void solve()
{
    int l, r;
    cin >> l >> r;
    if (r/3-(l-1)/3 >= 1) cout << "YES" << endl;
    else cout << "NO" << endl;
}
void solve()
{
    int l, r;
    cin >> l >> r;
    for (int i = l; i <= r; i++){
        if (i% 3 == 0){
            cout << "YES" << endl;
            return;
        }
    }
    cout << "NO" << endl;
}

小苯的好数

思路:区间查询很容易想到前缀和来做,因此就是考怎么来判断是否是好数,这里a[i]为偶数时, 肯定是好数,主要就是判断奇数怎么是好数,这里正解是要证明的(但我太菜了,只能打表来观察了),得出哪些奇数是好数,哪些不是:接下来是打表函数:

void solve()
{
	for (int i = 1; i<=99; i++){
		if (i%2 == 1){
			int sum = 1;
			for (int j = 2; j<i; j++){
				if (i%j == 0) sum += j;
			}
			if (sum % 2 == 0) cout << i << endl;
		}
	}
}

观察出只有平方数的因子和才能是偶数,所以就可以使用前缀和来解决此题

void solve() {
    int n, q;
    cin >> n >> q;
    vector<int> s(n + 1);
    auto check = [&](int x) -> bool {
        if(x % 2 == 0) return 1;
        int sq = sqrt(x);
        return sq * sq == x;
    };
    for(int i = 1, x; i <= n; i++) {
        cin >> x;
        s[i] = s[i - 1] + check(x);
    }
    while(q -- ) {
        int l, r;
        cin >> l >> r;
        cout << s[r] - s[l - 1] << endl;
    }
}

小苯的ovo

思路:动态规划,有点像背包问题,这里要找出cost为修改次数,并通过动态转移。注意dp数组的初始化

void solve()
{
    int n, m, k;
    cin >> n >> k;
    vector<vector<int>> dp(n+1, vector<int>(k+1, -INF));
    // dp[i][k]表示前i个字符,最多修改k次的最大个数
    string s;
    cin >> s;
    s = " "+s;
    if (n < 3){
        cout << 0 << endl;
        return;
    }
    dp[0][0] = 0, dp[1][0] = 0, dp[2][0] = 0;
    for (int i = 3; i<=n; i++){
        int num = (s[i-2]!='o')+(s[i-1]!='v')+(s[i]!='o');
        dp[i] = dp[i-1];
        for (int j = num; j<=k; j++){
            dp[i][j] = max(dp[i][j], dp[i-3][j-num]+1);
        }
    }
    cout << *max_element(dp[n].begin(), dp[n].end()) << endl;
}

 小苯的水瓶

思路:看到取最值应该使用二分,这里注意check函数的编写,并主要need大于k+m时提前结束,或者开_i128,防止爆longlong。

void solve()
{
    int n, m, k;
    cin >> n >> m >> k;
    vi a(n);
    for (int i = 0; i<n; i++) cin >> a[i];
    sort(a.begin(), a.end());
    auto check = [&](int x)->bool
    {
        int xu=0, need = 0;
        for (int i = 0; i<n; i++){
            xu += max(0ll, a[i]-x);
            need += max(0ll, x-a[i]);
            if (need > k+m) return false;
        }
        return k+min(xu, m)-need >= 0;
    };
    int l = 0, r = 1e9;
    while (l+1 < r){
        int mid = (l+r)/2;
        if (check(mid)) l = mid;
        else r = mid;
    }
    cout << l << endl;
}

小苯的旅行计划

思路:因为m的值不是很大,所以我们可以来枚举要使用魔法的边来找最小值,需要记录每条边有多少个人的记录,这里应该用树上边差分来统计, 再在枚举时取最大值,思路还是比较简单,但代码实现上还是有点难度。

struct LCA{
    vector<vector<int>> f;
    vector<int> sum, dep;
    vector<vector<pair<int, int>>> g;
    LCA(int n){
        f.resize(20, vector<int>(n+1));
        dep.resize(n+1), sum.resize(n+1);
        g.resize(n+1);
    }
    void dfs(int u, int fa){
        dep[u] = dep[fa] + 1;
        f[0][u] = fa;
        for (int i = 1; i<20; i++){
            f[i][u] = f[i-1][f[i-1][u]];
        }
        for (auto [v, w] : g[u]){
            if (v == fa) continue;
            sum[v] = sum[u]+w;
            dfs(v, u);
        }
    }
    int find(int u, int v){
        if (dep[u] < dep[v]) swap(u, v);
        for (int i = 19; i>=0; i--){
            if (dep[f[i][u]] >= dep[v]) u = f[i][u];
        }
        if (u == v) return u;
        for (int i = 19; i>=0; i--){
            if (f[i][u] != f[i][v]){
                u = f[i][u];
                v = f[i][v];
            }
        }
        return f[0][u];
    }
};
struct node{
    int u, v, w;
};
void solve(){
    int n, m;
    cin >> n >> m;
    LCA lca(n);
    vector<node> edge(n+1);
    for (int i=1,u,v,w; i<=n-1; i++){
        cin >> u >> v >> w;
        lca.g[u].push_back(make_pair(v, w));
        lca.g[v].push_back(make_pair(u, w));
        edge.push_back({u, v, w});
    }
    lca.dfs(1, 0);
    int cost = 0;
    vector<int> s(n+1);
    for (int i = 0,a,b; i<m; i++){
        cin >> a >> b;
        int ll = lca.find(a, b);
        s[a]++, s[b]++, s[ll]-=2;
        cost += (lca.sum[a]+lca.sum[b]-2*lca.sum[ll]);
    }
    auto dfs2 = [&](auto &&dfs2, int u, int fa)->void
    {
        for (auto [v, w]: lca.g[u]){
            if (v == fa) continue;
            dfs2(dfs2, v, u);
            s[u] += s[v];
        }
    };
    dfs2(dfs2, 1, 0);
    int mx = 0;
    for (auto [u, v, w]:edge){
        if (lca.dep[u] < lca.dep[v]) swap(u, v);
        mx = max(mx, s[u]*w);
    }
    cout << cost - mx << endl;
}
struct Edge{
    int u, v, w;
};
void solve()
{
    int n, m, k;
    cin >> n >> m;
    vector<vector<pair<int,int>>> g(n+1);//记录连接边
    vector<Edge> edge;   //记录m条边
    for (int i = 1; i<=n-1; i++){
        int u, v, w;
        cin >> u >> v >> w;
        g[u].pb(mp(v,w));
        g[v].pb(mp(u,w));
        edge.pb({u,v,w});
    }
    vector<vector<int>> f(20, vector<int>(n+1)); //父节点
    vector<int> dep(n+1), sum(n+1);  //深度     树的前缀和(从根到叶递增)
    auto dfs1= [&](auto && dfs1, int u, int fa)->void //倍增预处理
    {
        dep[u] = dep[fa]+1;
        f[0][u] = fa;
        for (int j = 1; j<20; j++){
            f[j][u] = f[j-1][f[j-1][u]];
        }
        for (auto & [v,w] : g[u]){
            if (v == fa) continue;
            sum[v] = sum[u] + w;
            dfs1(dfs1, v, u);
        }
    };
    dfs1(dfs1, 1, 0);
    auto LCA = [&](int u, int v)->int   //求最近公共祖先
    {
        if (dep[u] < dep[v]) swap(u,v);
        for (int j = 19; j>=0; j--){
            if (dep[f[j][u]] >= dep[v]) u = f[j][u];
        }
        if (u == v) return u;
        for (int j = 19; j>=0; j--){
            if (f[j][u] != f[j][v]){
                u = f[j][u];
                v = f[j][v];
            }
        }
        return f[0][u];
    };
    int cost = 0;
    vector<int> s(n+1);
    for (int i = 0, a, b; i<m; i++){
        cin >> a >> b;
        int lca = LCA(a,b);
        s[a]++, s[b]++, s[lca]-=2;
        cost += (sum[a]+sum[b]-2*sum[lca]); // 算出原本的边权前缀和(不使用魔法时,全部的需要的花费)
    }
    auto dfs2 = [&](auto && dfs2, int u, int fa)->void  //利用差分求每条边经过的次数
    {
        for (auto [v,w]:g[u]){
            if (v == fa) continue;
            dfs2(dfs2, v, u);
            s[u]+=s[v];
        }
    };
    dfs2(dfs2, 1, 0);
    int mx = 0;
    for (auto [u,v,w] : edge){
        if (dep[u] < dep[v]) swap(u,v);   // 这里是因为边权是下降到点的,所以要用深度更深的那个点
        mx = max(mx, s[u]*w);
    }
    cout << cost-mx << endl;
}

小苯的奇怪最短路

思路:

考虑枚举答案中的最大边 w,那么问题变成了最小边如何求。

让我们考虑最小生成树的求法,在合并连通块的过程中,如果 (u,v)连通则我们直接跳过,否则连上边,并给总权加上 (u,v)的权。

在本题中实际上也类似,我们给每个连通块都维护一个当前连通块中的最小边 mn,接着正常执行

𝑘𝑟𝑢𝑠𝑘𝑎𝑙求最小生成树,如果当前 1 和 n 已经连通,则我们就对 1,n所在的连通块的最小边 mn 加上当前边 w 取 min即可,在过程中要一直维护每个连通块中最小边的权 mn

struct edge{
    int u, v, w;
    bool operator < (const edge&e) const{
        return w < e.w;
    }
};
struct ufSet{
    vector<int> fa,siz;
    // fa[N]用于存储每个元素的父节点信息
    // siz[N]用于存储每个集合的大小
    vector<int> mn;
    void init(int n){
        fa.resize(n+5),siz.resize(n+5);
        mn.resize(n+5);
        for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1,mn[i]=INF;
    }
    int find(int x){
        if(x==fa[x]) return x;
        fa[x]=find(fa[x]);
        return fa[x];
    }
    bool mrg(int x,int y, int w){
        x=find(x),y=find(y);
        // if(x==y) return 0;
 
        if(siz[x]<siz[y]) swap(x,y);
        fa[y]=x,siz[x]+=siz[y];
        mn[x]=min({mn[x],w, mn[y]});
        return 1;
    }
}t;
void solve()
{
    int n, m, k;
    cin >> n >> m;
    t.init(n);
    vector<edge> e;
    for (int i = 0, u, v, w; i<m; i++){
        cin >> u >> v >> w;
        e.pb({u, v, w});
    }
    sort(e.begin(), e.end());
    int ans = INF;
    for (auto [u, v, w]: e){
        t.mrg(u, v, w);
        if (t.find(1) == t.find(n)){
            ans = min(ans, t.mn[t.find(1)]+w);
        }
    }
    if (ans > 1e17) ans = -1;
    cout << ans << endl;
}

 

相关文章:

  • 限制数据库字段长度的公用写法:length和like和rlike对于限制字段长度的原理与区别
  • 【技海登峰】Kafka漫谈系列(九)SpringBoot整合Kafka多数据源配置
  • BFS最短路径(十六)127. 单词接龙 困难
  • 爬虫案例十四js逆向中国五矿
  • 2.库函数的模拟实现
  • ES怎么通过客户端操作和查询/curl操作指令
  • DeepBI驱动的动态预算与库存联动调整策略
  • 当AI回答问题时,它的“大脑”里在炒什么菜?
  • LoRa无线通讯边缘网关-EG2000-数据上云和远程组网
  • Android电量与流量优化
  • npm、pnpm、cnpm、yarn、npx之间的区别
  • 我的创作纪念日:730天的技术写作之旅
  • 11 | 给 Gin 服务器添加中间件
  • 晨控CK-FR08与汇川H5U系列PLC配置EtherNet/IP通讯连接手册
  • 六、OpenGL中EBO的使用及本质
  • 【Godot4.3】斜抛运动轨迹曲线点求取函数
  • 时间序列模型(1):LSTNet
  • 解决ubuntu(jetpack)系统下系统盘存储不够的
  • MongoDB备份与还原
  • 2025年第十届数维杯大学生数学建模挑战赛参赛规则
  • 《AI×SCIENCE十大前沿观察》9:合成数据和数据基础设施
  • 来伊份:已下架涉事批次蜜枣粽产品,消费者可获额外补偿,取得实物后进一步分析
  • 韩国执政党总统候选人更换方案被否决,金文洙候选人资格即刻恢复
  • 云南多地突查公职人员违规饮酒:公安局门口开展酒精吹气测试
  • 理财经理泄露客户信息案进展:湖南省检受理申诉,证监会交由地方监管局办理
  • 上海国际电影节推出三大官方推荐单元,精选十部优秀影片