Description
Input
Output
Sample Input
1
5 7
1 2 2
1 4 1
2 4 2
4 3 2
2 3 1
4 5 1
1 5 2
Sample Output
5
No
Data Constraint
赛时
最小生成树打一遍,之后贪心的认为选一条没有选的边中最小的边,然后再打一遍最小生成树,竟然还有62分!之后把long long开了有82分!!!
正解
很多方法,我的方法大概是,先最小生成树,之后对于每个没选的边u,v,w,我们查找u到v的路径上最大的边权,那么这个边权一定不可能大于w,那么考虑能不能=w,如果可以的话说明我们可以用这个没选的边来替换这个最大的边权。这时就不是唯一的。然后树上任意两点路径最大值就用倍增,树剖来搞都行,因为练练手,所以后面我就搞了树剖(调了好几个晚上)
码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200007
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
int n,m,cnt,f[N],head[N<<1],maxtr[N<<2],w[N],dep[N],size[N],hson[N],top[N],id[N],num;
ll ans;
bool b[N];
struct node{
int u,v,w;
}e[N<<1];
bool cmp(node a,node b){return a.w<b.w;}
struct tree{
int to,w,nxt;
}tree[N<<1];
void init(){
cnt=0;num=0;ans=0;
mem(f);mem(head);mem(maxtr);;
mem(w);mem(dep);mem(size);mem(hson);
mem(top);mem(id);mem(b);mem(e);mem(tree);
}
int get(int x){return x==f[x]?x:f[x]=get(f[x]);}
void Kruskal(){
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++) f[i]=i;
int num=0;
for(int i=1;i<=m;i++){
int fau=get(e[i].u),fav=get(e[i].v);
if(fau!=fav){
num++;
b[i]=true;//加入了最小生成树
ans+=e[i].w;
f[fau]=fav;
}
if(num==n-1) break;
}
printf("%lld\n",ans);
}
void add(int u,int v,int w){
tree[++cnt].to=v;
tree[cnt].w=w;
tree[cnt].nxt=head[u];
head[u]=cnt;
}
void build(int np,int l,int r){
if(l+1==r){
maxtr[np]=w[r];
return;
}
int mid=l+r>>1;
build(np<<1,l,mid);//由于找边权,所以树开的有区别
build(np<<1|1,mid,r);
maxtr[np]=max(maxtr[np<<1],maxtr[np<<1|1]);
}
int query(int np,int l,int r,int x,int y){
if(x==y) return 0;
if(x<=l&&y>=r)
return maxtr[np];
int mid=l+r>>1;
int answer=0;
if(x<mid) answer=max(answer,query(np<<1,l,mid,x,y));
if(y>mid) answer=max(answer,query(np<<1|1,mid,r,x,y));
return answer;
}
int pd(int x,int y){
int answer=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
swap(x,y);
answer=max(answer,query(1,1,num,id[top[x]],id[x]));
answer=max(answer,w[id[top[x]]]);//我们将这条重链跳完后就要跳到这条重链顶端的父亲,那么这时后重链顶端和它父亲之间的权还没有比,所以这里要比一下。
x=f[top[x]];
}
if(dep[x]>dep[y])
swap(x,y);
answer=max(answer,query(1,1,num,id[x],id[y]));
return answer;
}
void dfs_son(int fa,int x){
f[x]=fa;
dep[x]=dep[fa]+1;
size[x]=1;
hson[x]=0;
for(int i=head[x];i;i=tree[i].nxt){
int v=tree[i].to;
if(v==fa) continue;
dfs_son(x,v);
size[x]+=size[v];
if(size[hson[x]]<size[v])
hson[x]=v;
}
}
void dfs_chain(int tp,int x){
top[x]=tp;
id[x]=++num;
if(hson[x]) dfs_chain(tp,hson[x]);
for(int i=head[x];i;i=tree[i].nxt){
int v=tree[i].to;
if(v==f[x]||v==hson[x]) continue;
dfs_chain(v,v);
}
}
void Tree_chain_partition(){
dfs_son(0,1);//第一遍dfs找子树大小,父亲,重儿子
dfs_chain(1,1);//第二遍找重链,重链顶点,新的编号
for(int i=1;i<=m;i++)//赋边权值
if(b[i]){
if(dep[e[i].u]<dep[e[i].v])
w[id[e[i].v]]=e[i].w;
else w[id[e[i].u]]=e[i].w;
}
build(1,1,num);//建线段树
bool bz=false;
for(int i=1;i<=m;i++)
if(!b[i]){//没有加入生成树的树
int k=pd(e[i].u,e[i].v);//u到v的路径上的最大边权
if(k==e[i].w){
bz=true;
break;
}
}
if(bz) printf("No\n");
else printf("Yes\n");
}
int main(){
int T;
scanf("%d",&T);
while(T--){
init();//初始化
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
Kruskal();//一遍最小生成树
memset(f,0,sizeof(f));//数组清除一下
for(int i=1;i<=m;i++)
if(b[i])
add(e[i].u,e[i].v,e[i].w),add(e[i].v,e[i].u,e[i].w);//重新加边
Tree_chain_partition();//树剖
}
}
感觉有些地方不够精炼啊,时间也比较慢,还是要再提升。