CF Round 617-F Berland Beauty //暴力dfs+路径记录

题目链接

https://codeforces.com/contest/1296/problem/F

题意

简而言之,就是先给一颗有 n n n 个顶点的树 ( n − 1 n-1 n1条边),现在知道 m m m 条路径上的边的最小值,让你给每条边赋值,以至满足这 m m m条限制,不存在合法方案就输出 − 1 -1 1.

思路

由于 n n n 比较小,所以有一种暴力的做法,我们可以建完树后,枚举所有顶点预处理出任意一个顶点作为根到其他 n − 1 n-1 n1 个顶点时该顶点的前驱顶点编号,这个实现比较简单,复杂度 O ( n 2 ) O(n^2) O(n2)
然后对这 m m m 条路径的最小值从小到大排个序,我们从小的开始处理,这样是为了保证当有两条路径叠加时,叠加的那部分一定是大的那个,这样才有可能合法。将每条路径所有边都赋值为题目所给值。
最后就是重新判断这 m m m 条路径边的最小值是否满足题意了,当发现有一条不满足题意,就输出 − 1 -1 1 结束程序。否则最后把这 n − 1 n-1 n1 条边输出,注意有些边始终没有被赋值,就将它赋值为 1 e 6 1e6 1e6(边界值保证合法)。

细节

在处理 m m m 条路径的时候,我们还要找对应边的编号,才好赋值,如果每条边的编号也尝试着暴力找的话会超时 O ( m ∗ n 2 ) O(m*n^2) O(mn2),存 m a p map map里也超时 因为多了一个 l o g log log ,所以必须 O ( 1 ) O(1) O(1) 查询边对应编号,我们可以在开始找前驱的时候顺便把边的编号也维护了,具体看代码吧。

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=5e3+10;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
#define pb push_back
#define ft first
#define sd second
#define ms(x,y) memset(x,y,sizeof(x))
int n,m;
vector<P> g[maxn];
struct edge{
    int u,v,w;
}e[maxn];
bool cmp(edge x,edge y){
    return x.w<y.w;
}
//pre[u][v][0]表示根为u时,到v的路径上,v的前驱结点与v所连边的编号
//pre[u][v][1]表示根为u时,到v的路径上,v的前驱结点的编号
int pre[maxn][maxn][2];
int ans[maxn];
void dfs(int u,int fa,int root,int id){
    pre[root][u][0]=id;
    pre[root][u][1]=fa;
    for(int i=0;i<(int)g[u].size();i++){
        int v=g[u][i].ft,t=g[u][i].sd;
        if(v==fa)continue;
        dfs(v,u,root,t);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        g[u].pb(P(v,i));
        g[v].pb(P(u,i));
    }
    for(int i=1;i<=n;i++){//预处理所有点对间的信息
        dfs(i,0,i,0);
    }
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    }
    sort(e,e+m,cmp);
    for(int i=0;i<m;i++){//暴力赋值
        int u=e[i].u,v=e[i].v,w=e[i].w;
        for(int j=v;j!=u;j=pre[u][j][1]){//倒着找
            int id=pre[u][j][0];//对应边的编号
            ans[id]=w;
        }
    }
    for(int i=0;i<m;i++){
        int u=e[i].u,v=e[i].v,w=e[i].w;
        int mi=inf;
        for(int j=v;j!=u;j=pre[u][j][1]){
            int id=pre[u][j][0];
            if(ans[id]==0)ans[id]=1e6;
            else {
                mi=min(mi,ans[id]);
            }
        }
        if(mi!=w){//不符合题目限制
            puts("-1");return 0;
        }
    }
    for(int i=1;i<n;i++){
        if(ans[i]==0)ans[i]=1e6;
        printf("%d%c",ans[i],i==n-1?'\n':' ');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值