题目链接
https://codeforces.com/contest/1296/problem/F
题意
简而言之,就是先给一颗有 n n n 个顶点的树 ( n − 1 n-1 n−1条边),现在知道 m m m 条路径上的边的最小值,让你给每条边赋值,以至满足这 m m m条限制,不存在合法方案就输出 − 1 -1 −1.
思路
由于 n n n 比较小,所以有一种暴力的做法,我们可以建完树后,枚举所有顶点预处理出任意一个顶点作为根到其他 n − 1 n-1 n−1 个顶点时该顶点的前驱顶点编号,这个实现比较简单,复杂度 O ( n 2 ) O(n^2) O(n2)。
然后对这 m m m 条路径的最小值从小到大排个序,我们从小的开始处理,这样是为了保证当有两条路径叠加时,叠加的那部分一定是大的那个,这样才有可能合法。将每条路径所有边都赋值为题目所给值。
最后就是重新判断这 m m m 条路径边的最小值是否满足题意了,当发现有一条不满足题意,就输出 − 1 -1 −1 结束程序。否则最后把这 n − 1 n-1 n−1 条边输出,注意有些边始终没有被赋值,就将它赋值为 1 e 6 1e6 1e6(边界值保证合法)。
细节
在处理 m m m 条路径的时候,我们还要找对应边的编号,才好赋值,如果每条边的编号也尝试着暴力找的话会超时 O ( m ∗ n 2 ) O(m*n^2) O(m∗n2),存 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;
}