【USACO 2009 JAN GOLD】安全路径

本文介绍了一种解决奶牛在农场中避开障碍(Gremlins)寻找最短路径的问题,利用最短路径树结合树链剖分和线段树进行优化处理。

时间限制 : 20000 MS 空间限制 : 65536 KB

问题描述

Gremlins最近在农场上泛滥,它们经常会阻止牛们从农庄(牛棚_1)走到别的牛棚(牛_i的目的地是牛棚_i)。每一个gremlin只认识牛_i并且知道牛_i一般走到牛棚_i的最短路经。所以它们在牛_i到牛棚_i之前的最后一条牛路上等牛_i,当然,牛不愿意遇到Gremlins,所以准备找一条稍微不同的路经从牛棚_1走到牛棚_i,所以,请你为每一头牛_i找出避免gremlin_i的最短路经的长度。
和以往一样,农场上的M (2 <= M <= 200,000)条双向牛路编号为1..M并且能让所有牛到达它们的目的地,N(3 <= N <=100,000)个编号为1..N的牛棚。牛路i连接牛棚a_i (1 <= a_i <= N)和b_i (1 <= b_i <=N)并且需要时间t_i (1 <=t_i <=1,000)通过。没有两条牛路连接同样的牛棚,所有牛路满足a_i!=b_i。在所有数据中,牛_i使用的牛棚_1到牛棚_i的最短路经是唯一的。
以下是一个牛棚,牛路和时间的例子:这里写图片描述

输入格式

第一行:两个空格分开的数N和M;
第2..M+1行:三个空格分开的数a_i, b_i,和t_i

输出格

第1..N-1行:第i行包含一个数,从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间。如果这样的路经不存在,输出-1。

样例输入

4 5
1 2 2
1 3 2
3 4 4
3 2 1
2 4 3

样例输出

3
3
6

提示

20%的数据满足,N<=200;
50%的数据满足,N<=3000
100%的数据满足,N<=100,000

题解

本题中,奶牛们的原路径由从1号点出发到每个点的最短路组成,这提示我们可能需要建一棵最短路树,并在上面做点文章。显然,按照题目的要求,奶牛会经过不在最短路树上的另一条边绕过去。设我们要到的点为kk,有一条不在最短路上的边为(x,y)t=LCA(x,y)t=LCA(x,y)kktyy的路径上。那么奶牛绕行的距离可表示为dis[x]+len[x][y]+dis[y]dis[k],可以发现只要dis[x]+len[x][y]+dis[y]dis[x]+len[x][y]+dis[y]最小,所求答案就可最小。因此我们枚举不在最短路树的每一条边,更新两端点与LCALCA间的点即可(注意不能更新LCALCA)。显然可以树链剖分+线段树维护最小值。(图片引自何老板PPT)
这里写图片描述
顺便复习了一拨dijkstra+heap,才发现自己一直写的是SPFA式的假dijkstra……QAQ

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> pii;
const int maxn=1e5+5;
const int inf=1e9;
int a,b,c,n,m,cnt=1,dis[maxn],fro[maxn],Last[maxn],St[maxn<<2],End[maxn<<2],Len[maxn<<2],Next[maxn<<2];
int vt,fa[maxn],dep[maxn],dfn[maxn],siz[maxn],son[maxn],top[maxn];
int lazy[maxn<<2],tree[maxn<<2];
bool vis[maxn],mark[maxn<<2];
priority_queue<pii,vector<pii>,greater<pii> > q;
void Addedge(int x,int y,int z)
{
    End[++cnt]=y,St[cnt]=x,Len[cnt]=z;
    Next[cnt]=Last[x],Last[x]=cnt;
}
void dijkstra(int x)
{
    for(int i=1;i<=n;i++) dis[i]=inf;
    dis[1]=0,q.push(make_pair(0,1));
    while(!q.empty())
    {
        int x=q.top().second;q.pop();
        if(vis[x]) continue;vis[x]=true;
        for(int i=Last[x];i;i=Next[i])
        {
            int y=End[i];
            if(dis[x]+Len[i]<dis[y])
            {
                dis[y]=dis[x]+Len[i],q.push(make_pair(dis[y],y));
                mark[fro[y]]=false,fro[y]=i,mark[i]=true;
            }
        }
    }
}
void Get_siz(int x)
{
    dep[x]=dep[fa[x]]+1,siz[x]=1;
    for(int i=Last[x];i;i=Next[i])
        if(mark[i])
        {
            int y=End[i];
            if(y==fa[x]) continue;
            fa[y]=x,Get_siz(y),siz[x]+=siz[y];
            if(siz[y]>siz[son[x]]) son[x]=y;
        }
}
void Get_dfn(int x,int anc)
{
    if(!x) return;
    dfn[x]=++vt,top[x]=anc;
    Get_dfn(son[x],anc);
    for(int i=Last[x];i;i=Next[i])
        if(mark[i])
        {
            int y=End[i];
            if(y==fa[x]||y==son[x]) continue;
            Get_dfn(y,y);
        }
}
namespace segment
{
    void putdown(int p)
    {
        tree[p<<1]=min(tree[p<<1],lazy[p]);
        tree[p<<1|1]=min(tree[p<<1|1],lazy[p]);
        lazy[p<<1]=min(lazy[p<<1],lazy[p]);
        lazy[p<<1|1]=min(lazy[p<<1|1],lazy[p]);
        lazy[p]=inf;
    }
    void build(int p,int l,int r)
    {
        if(l<r)
        {
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
            tree[p]=lazy[p]=inf;
        }
        else tree[p]=lazy[p]=inf;
    }
    void modify(int p,int l,int r,int x,int y,int k)
    {
        if(x<=l&&r<=y)
        {
            lazy[p]=min(lazy[p],k),tree[p]=min(tree[p],k);
            return;
        }
        if(lazy[p]<inf) putdown(p);
        int mid=(l+r)>>1;
        if(x<=mid&&y>=l) modify(p<<1,l,mid,x,y,k);
        if(y>mid&&x<=r) modify(p<<1|1,mid+1,r,x,y,k);
    }
    int query(int p,int l,int r,int x)
    {
        if(l==r) return tree[p];
        if(lazy[p]<inf) putdown(p);
        int mid=(l+r)>>1;
        if(x<=mid) return query(p<<1,l,mid,x);
        return query(p<<1|1,mid+1,r,x);
    }
}
int Get_lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]]) swap(x,y);
        y=fa[top[y]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    return x;
}
void change(int x,int y,int z)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]]) swap(x,y);
        segment::modify(1,1,n,dfn[top[y]],dfn[y],z);
        y=fa[top[y]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    segment::modify(1,1,n,dfn[x]+1,dfn[y],z);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        Addedge(a,b,c),Addedge(b,a,c);
    }
    dijkstra(1),Get_siz(1),Get_dfn(1,1);
    segment::build(1,1,n);
    for(int i=2;i<=cnt;i++)
        if(!mark[i])
        {
            int lca=Get_lca(St[i],End[i]);
            change(End[i],lca,dis[St[i]]+dis[End[i]]+Len[i]);
        }
    for(int i=2;i<=n;i++)
    {
        int temp=segment::query(1,1,n,dfn[i]);
        if(temp<inf) printf("%d\n",temp-dis[i]);
        else puts("-1");
    }
    return 0;
}
<think>好的,我现在需要帮助用户查找关于USACO 2024年1月竞赛中“Mooball Teams III”问题的解决方案或资料。首先,我应该先检查现有的引用内容,看看是否有相关的信息。根据用户提供的三个引用,引用[1]是关于循环舍入问题,引用[2]是多数意见问题,引用[3]是关于抓捕问题的铂金级题目。这三个引用中并没有直接提到“Mooball Teams III”的问题,所以可能需要寻找其他途径。 接下来,用户可能需要了解该问题的具体内容和要求。由于题目名称中的“Mooball Teams III”可能涉及组合优化、动态规划或图论等算法。例如,可能有关于队伍组合、最优匹配或资源分配的问题。我需要考虑常见的USACO题目类型,推测可能的解题思路。 考虑到USACO竞赛的时间,2024年1月的比赛可能较新,官方题解可能还未公开。通常USACO在比赛结束后一段时间会发布官方解答,但有时可能需要参赛者自己等待。因此,建议用户关注USACO官网或论坛,如Codeforces、Reddit的r/usaco,或者USACO Guide等平台,这些地方可能会有参赛者分享思路。 另外,可以建议用户检查问题陈述中的输入输出样例,尝试逆向推导算法。例如,如果问题涉及最大匹配,可能需要使用贪心或匈牙利算法;如果是动态规划,可能涉及状态转移方程的设计。同时,建议用户先尝试暴力解法,再逐步优化,这是常见的解题步骤。 还需要提醒用户注意数据规模,不同的算法时间复杂度是否适合题目给定的数据范围。例如,若数据量较大,可能需要O(n log n)或更优的算法,而非暴力解法。 最后,由于用户提供的引用中没有直接相关的资料,可能需要告知用户当前无法提供具体解决方案,但可以给予一般性的解题策略和资源获取建议,帮助用户自行进一步探索。</think>关于USACO 2024年1月竞赛的“Mooball Teams III”问题,目前尚未在公开平台找到完整的官方解决方案。不过可以基于USACO竞赛常见题型特点,提供以下解题思路参考: ### 可能涉及的知识点 1. **组合数学**:若题目涉及队伍组合或排列限制,可能需要使用组合数公式$C(n,k)$或容斥原理 2. **贪心算法**:在资源分配类问题中,可能需要按特定规则排序后贪心选择 3. **动态规划**:若存在状态转移关系,可能需要构建二维DP数组,例如$dp[i][j]$表示前i个元素选择j个的方案数 4. **图论建模**:若涉及团队关系约束,可能需要构建二分图进行最大匹配计算 ### 推荐实践步骤 1. **分析约束条件**:仔细阅读题目中的特殊条件,如: - 队伍人数限制 - 队员间兼容性规则 - 时间/空间复杂度要求 2. **输入输出样例解析**:通过逆向工程理解核心算法逻辑 ```python # 示例输入可能格式 3 2 # 3名队员,需组建2人队伍 1 2 # 兼容对 2 3 ``` 3. **基础代码框架**(以组合问题为例): ```python n, k = map(int, input().split()) compatible = [[] for _ in range(n+1)] # 构建兼容关系图 for _ in range(m): a, b = map(int, input().split()) compatible[a].append(b) compatible[b].append(a) # 动态规划解法示例 dp = [[0]*(k+1) for _ in range(n+1)] dp[0][0] = 1 for i in range(1, n+1): for j in range(k+1): dp[i][j] = dp[i-1][j] # 不选当前队员 if j > 0: dp[i][j] += dp[i-1][j-1] * valid_options(i) # 选当前队员的合法情况 ``` ### 资源获取建议 1. **USACO官方论坛**:竞赛结束后约2-3个月会发布题解 2. **USACO Guide**(https://usaco.guide/)查看相似题目解析 3. **Codeforces博客**搜索"USACO 2024 Jan"关键词 4. **GitHub代码库**:部分参赛者会公开AC代码(注意遵守竞赛规则)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值