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;
}
完结散花!