2019牛客多校第四场A/K

本文探讨了两种算法问题:一是求解树中任意两点间最长距离,即树的直径,用于解决特定点到多个关键点距离最小化问题;二是寻找字符串中所有长度为300倍数的子串,采用前缀和技巧进行高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A.给你一张n个点n-1条边的图,和k个关键点。求一个点到所有关键点距离最大值的最小为多少。

乍一看像是对答案二分,但是考虑两个相距最远的关键点,假设他们的距离为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;
}

K 给你一个字符串 ,求所有是300的倍数的子串。

首先考虑是3的倍数,要求一个区间是三的倍数则将他的数位前缀和%3 出现重复的时候,则区间l+1~r是3的倍数

则我们要求的区间s[r],s[r-1]均为0的时候,统计0~r-2中有多少满足sx=0,1,2在s[r-1]更新答案,

类比于30的倍数应该当s[r]=0时 在s[r]更新答案。

3000的倍数应该当s[r]=0,s[r-1]=0,s[r-2]=0时在s[r-2]更新答案。

特别 0 也是300的倍数所以每次遇到一个0 ,ans++;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
char s[100016];
int x[100016];
ll mp[6];
ll ans=0;
int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    mp[0]=1;
    for(int i=1;i<=len;i++)
    {
        x[i]=((s[i]-'0')+x[i-1])%3;
        if(s[i]=='0')ans++;
        if(s[i]=='0'&&s[i+1]=='0')
        {
            ans+=mp[x[i]];
            //ans++;
        }
        mp[x[i]]++;
    }
    cout<<ans<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值