NOIP 模拟赛 day12 T4 跑路 水题解

Description

给你 \(N\) 个点, \(M\) 条边一个图,在可重复走的情况下,问从第一个点到第 \(N\) 个点的距离在二进制下 \(1\) 的个数最少的情况有多少个 \(1\)

\(N\leq 50\ \ \ M\leq 10^4\ \ \ ans< 2^{31}\)

Solution

其实最烦的还是可以重复走,很显然不管怎么样,我们都无法把他的复杂度降下来,所以我们考虑如何去避免接触他。

题意表明答案和距离本身的大小没有关系,而是跟其二进制下 \(1\) 的数量有关,所以我们考虑设这么一个状态: \(f_{i,j,k}\) 表示从点 \(i\) 到点 \(j\) 能否走 \(2^k\) 步到达。这个数组我们发现可以通过一个类似(本质上就是一样) floyed 的过程求解,只不过最外面要从小到大枚举 \(k\ \ (1-31)\) ,最后在跑一遍 floyed 找答案就行了。

但是按照超脑的说法, floyed N=100 的时候复杂度直接爆炸,所以我们只能祈求,这道题时间复杂度 \(O(31\cdot N^3)\) 应该不会爆炸吧

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+10;
ll t,n,fa[N],siz[N],ans;
struct mdzz{
	ll u,v,w;
	bool operator < (const mdzz& it) const{
		return w<it.w;
	}
}edge[N];
inline ll read(){
	ll s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
inline ll find(ll x){
	if(fa[x]==x)return fa[x];
	return fa[x]=find(fa[x]);
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	t=read();
	while(t--){
		n=read();ans=0;
		for(int i=1;i<=n;++i){
			fa[i]=i;siz[i]=1;
		}
		for(int i=1;i<n;++i){
			int u=read(),v=read(),w=read();
			edge[i]=(mdzz){u,v,w};ans+=w;
		}
		sort(edge+1,edge+n);
		for(int i=1;i<n;++i){
			int uu=find(edge[i].u);
			int vv=find(edge[i].v);
			ans+=(siz[uu]*siz[vv]-1ll)*(edge[i].w+1ll);
			fa[vv]=uu;siz[uu]+=siz[vv];siz[vv]=0;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

完结散花!

posted @ 2021-07-22 18:44  Illusory_dimes  阅读(46)  评论(0编辑  收藏  举报