2017 ACM/ICPC Asia Regional Shenyang Online transaction

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6201

题目大意:一些城市及其之间的道路构成了一棵树。对于某一种物品,在城市 i 的价格是 ai单位金钱 ,而在道路上每走1单位长度就会花费1单位金钱。问任意选定买入该物品的城市和卖出该物品的城市,每个物品最大获利是多少。

可以发现,如果一个城市的价格加上旅途费用后的单价比旅途终点的价格低,则在终点买入肯定不如在起点买入,于是可以用起点的单价加旅途的费用作为终点的新的单价,然后继续更新其他城市的单价。显然,如果从价格较低的城市开始依次更新其他城市的单价,然后比对每个城市的最小单价和原来的价格之差得到答案。

于是就有了一个比较裸的算法,从单价小的城市更新单价大的城市就可以了。

但是,这样直接写会被特殊构造的数据卡成TLE(今天人品还不错,没有被卡,笑CRY.......

如下面的数据点:

1
8
1 2 3 4 1000 1001 10002 1003
1 5 100
2 5 98
3 5 96
4 5 94
5 6 1
6 7 1
7 8 1

在这个数据点里,该算法会先用节点1更新节点5到节点8,然后又会用节点2更新节点5到节点8,然后是节点3,节点4。这个算法的实际时间复杂度已经达到了O(n^2),妥妥地会TLE

但是可以在这个基础上进行改进。

令F( x)代表以节点x为起点,到其他城市的单价减距离的最大值。

显然如果以节点x进行搜索完一次以后,不可能再被其他点更新,于是可以确定每个点只会被搜索一次,于是算法时间复杂度是O(n)的,可以确定不会被特殊数据卡掉

代码:

#include <bits/stdc++.h>

using namespace std;
inline void read(int &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

inline void read(long long &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

int const maxn=200000;
int const maxm=500000;

int pre[ maxm ], now [ maxn ] ,son [ maxm ] , v[ maxm] ,tot;
bool vis[maxn];
int n;
int tmp[maxn];
int ttt[maxn];
struct zy{
int v,id;
}a[maxn];

void build(int a ,int b, int c ){
pre[++tot]=now[a];
now[a]=tot;
son[tot]=b;
v[tot]=c;
}

int Max[ maxn ];
int cnt=0;
void dfs(int x,int fa){
if (vis[x])
    return ;
for (int p=now[x];p;p=pre[p])
    if (son[p]!=fa)
    {
        if ( tmp[ son[p] ] > tmp[ x ]+v[p])
            dfs( son[p] , x );
        Max[ x ] = max( Max[ x ] , Max[ son[p] ] -v[p] );
    }
cnt++;
vis[x]=1;
}

bool cmpv(zy A , zy B){
return A.v<B.v;
}

void doit(){
read(n);
tot=0;
memset(now,0,sizeof(now));
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++)
{
    read(a[i].v);
    a[i].id=i;
    tmp[i]=a[i].v;
    ttt[i]=a[i].v;
}
for (int i=1;i<n;i++)
    {
        int a,b,c;
        read(a); read(b); read(c);
        build( a,b,c);
        build( b, a,c);
    }
sort(a+1,a+n+1,cmpv);
for (int i=1;i<=n;i++)
    Max[i]=tmp[i];
for (int i=1;i<=n;i++)
    if (!vis[ a[i].id ])
        dfs( a[i].id , 0 );
int ans=0;
for (int i=1;i<=n;i++)
    ans=max(Max[ i ]-tmp[i],ans);
printf("%d\n",ans);
}



int main(){
    int T;
    read(T);
    for (int i=1;i<=T;i++)
        doit();
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值