2019牛客多校训练营第四场补题

J题: Free   https://blog.csdn.net/canxuezhinuanyang/article/details/97671247

题意:给你n个城市,m条道路,经过每一条要花费这条路的代价,现给你k个机会,使得最多k条路的代价为0,问从起点s到终点t花费的最少代价

思路:据说是分层图最短路经典裸题 :https://www.cnblogs.com/wizarderror/p/11262719.html

但是考虑两个相距最远的关键点,假设他们的距离为d,那么答案肯定为(d+1)/2。

如果有一点到中心点的距离超过了(d+1)/2 ,那么这个点会成为最远关键点对中的一个。矛盾。

所以题目就变成了如何求最远的两个关键点的距离。

考虑如何求树的直径,首先取一个根节点通过bfs找到离他最远的叶子节点p,然后将p当做根节点再跑一遍BFS

求出离这个点最远的叶子节点q,那么从p到q的这条路径就是树的直径。

那么两个最远的关键点的距离就相当求出将关键点当成叶子节点的一棵树的直径,我们取一个点当根节点然后

BFS找到离他最远的关键点p 以这个关键点p为根节点再跑一遍BFS求出离这个关键点最远的关键点q,则p到q的路

径就是我们要找关键点最远距离d 然后 ans=(d+1)/2更新答案。

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,k;
vector<int>g[maxn];
int vis[maxn],flag[maxn],dis[maxn];
int bfs(int x)
{
    int ans;
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    queue<int>q;
    q.push(x);
    vis[x]=1;
    dis[x]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<g[u].size();i++)
        {
            int v=g[u][i];
            if(!vis[v])
            {
                dis[v]=dis[u]+1;
                if(flag[v])ans=v;
                vis[v]=1;
                q.push(v);
            }
        }
    }
    return ans;
}
int main()
{
    cin>>n>>k;
    int u,v;
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    int y;
    for(int i=1;i<=k;i++)
    {
        scanf("%d",&y);
        flag[y]=1;
    }
    int s,t;
    s=bfs(1);
    t=bfs(s);
    cout<<(dis[t]+1)/2<<endl;
    return 0;
}
View Code

 

K题: Number

题意:求子串中的是300的倍数的子串个数。

分析:可以拆成100 和 3 的倍数

假设给定的整数为 : 1 0 1 0 0 1 2 0 0 0
前缀和对3取余的结果:1 1 2 2 2 0 2 2 2 2

而两个相等的余数中间都可以算作一次答案,并且在遇到连续两个0后进行计算,就可以了。

这就是用前缀和处理符合条件子串个数的技巧

注意关键的一个易漏点: 一定要记得 cnt[ 0 ]  初始值就得是 1 ,否则会少算从头开始取的情况!

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5+5;
int a[maxn];
string s;
long long ans;
int cnt[3];

int main(){
    cin>>s;
    int len=s.length();
    long long sum=0;
    cnt[0] = 1;     //这个非常关键,非常容易漏掉
    for(int i=0; i<len; i++){
        if(s[i]=='0') ans++;
        sum += s[i]-'0';
        sum%=3;
        if(s[i]=='0' && s[i+1]=='0'){
            ans += cnt[sum];
        }
        cnt[sum]++;
    }
    cout<<ans<<endl;
}
View Code

 

转载于:https://www.cnblogs.com/-Zzz-/p/11532905.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值