首先看到这题, 由于要最大, 肯定是求最大生成树
那么 o(n2) dfs 求任意点对之间的最小边是可以想到的
但是看看数据范围肯定TLE
于是暴力出来咯, 不过要注意query的时候判断的时候要 m+-1 但是递归下去要用m , 可以画图举特例分析
1AC 代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smin(x,tmp) x=min((x),(tmp)) const int INF=0x3f3f3f3f; const int maxn=10005; const int maxm=50005; int fa[maxn]; int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } inline bool union_find(int x,int y) { int t1=find(x),t2=find(y); if(t1==t2) return false; fa[t2]=t1; return true; } map <pair<int,int>,int> g; struct Edge { int to,next; int val; }edge[maxm<<1]; int head[maxn]; int maxedge; inline void addedge(int u,int v,int c) { edge[++maxedge]=(Edge){v,head[u],c}; head[u]=maxedge; edge[++maxedge]=(Edge){u,head[v],c}; head[v]=maxedge; g[make_pair(u,v)]=c; g[make_pair(v,u)]=c; } struct Road { int from,to; int cost; bool operator < (const Road t) const { return cost>t.cost;// querying the Biggest MST !!!! } }road[maxm]; int n,m; int f[maxn],son[maxn],size[maxn],depth[maxn]; int top[maxn],id[maxn],rid[maxn]; int maxnode;//for segment tree (one-demensional array) int dfs1(int u,int father,int deep) { f[u]=father,size[u]=1,depth[u]=deep; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(v==father) continue; size[u]+=dfs1(v,u,deep+1); if(!son[u]||size[son[u]]<size[v]) son[u]=v; } return size[u]; } void dfs2(int u,int tp) { top[u]=tp;id[u]=++maxnode;rid[maxnode]=u;// non de mixer la id-rid !! if(son[u]) dfs2(son[u],tp); for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(v==f[u]||v==son[u]) continue; dfs2(v,v); } } int tree[maxn<<2];//min void build(int root,int l,int r) { if(r-l==1) { tree[root]=g[make_pair(rid[l],rid[r])]; return; } int m=(l+r)>>1; build(root<<1,l,m); build(root<<1|1,m,r); tree[root]=min(tree[root<<1],tree[root<<1|1]); } int query(int root,int l,int r,int x,int y)//query min { if(x==y) return INF;//must!! when query the same node !!!!!! if(x<=l&&r<=y) return tree[root]; int m=(l+r)>>1; int t1=INF,t2=INF; if(x<=m-1&&l<=y) t1=query(root<<1,l,m,x,y);//here too, use x<=m-1 in case stucking at m if(y>=m+1&&r>=x) t2=query(root<<1|1,m,r,x,y);//be conscious of m or m+1, query m but judge m+1 return min(t1,t2); } int Find(int u,int v)//find min { int t1=top[u],t2=top[v]; int ret=INF; while(t1^t2) { if(depth[t1]<depth[t2]) swap(t1,t2),swap(u,v); smin(ret,query(1,1,maxnode,id[t1],id[u])); smin(ret,g[make_pair(t1,f[t1])]); u=f[t1]; t1=top[u]; } if(depth[u]<depth[v]) swap(u,v); return min(ret,query(1,1,maxnode,id[v],id[u])); } inline void init() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].cost); for(int i=1;i<=n;i++) fa[i]=i; memset(head,-1,sizeof(head)); maxedge=-1; } void kruskal() { sort(road+1,road+m+1); int pos=1,tot=0; while(pos<=m && tot^(n-1)) { if(union_find(road[pos].from,road[pos].to)) tot++,addedge(road[pos].from,road[pos].to,road[pos].cost); pos++; } } bool vis[maxn];//for union_find void build_forest() { for(int i=1;i<=n;i++) { int father=find(i); if(!vis[father]) { vis[father]=true; dfs1(father,0,1); dfs2(father,father); } } build(1,1,maxnode); } int main() { freopen("truck.in","r",stdin); freopen("truck.out","w",stdout); init(); kruskal(); build_forest(); int q; scanf("%d",&q); while(q--) { int x,y; scanf("%d%d",&x,&y); if(find(x)^find(y)) printf("-1 "); else printf("%d ",Find(x,y)); } return 0; }
但是NOIP正解一定不是链剖, 此题要用到 LCA 的 ST 倍增算法, 并且属于精确的查询,没有重叠部分, 可以用sum等进行替换, 边dfs边更新
用 f[i][j] = f[f[i][j-1]][j-1] 保存 i 前面的第2i 节点, 与普通 RMQ 不同
用 dp[i][j] = min ( dp[i][j-1] , dp[f[i][j-1]][j-1] ) 来维护倍增的最小值 ( 当然 sum 也一样 , 因为精确求范围, 满足区间加 )
AC代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smin(x,tmp) x=min((x),(tmp)) #define smax(x,tmp) x=max((x),(tmp)) const int INF=0x3f3f3f3f; const int maxn=10005; const int maxm=50005; const int maxd=20; int fa[maxn]; int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } inline bool union_find(int x,int y) { int t1=find(x),t2=find(y); if(t1==t2) return false; fa[t2]=t1; return true; } struct Edge { int to,next; int val; }edge[maxm<<1]; int head[maxn]; int maxedge; inline void addedge(int u,int v,int c) { edge[++maxedge]=(Edge){v,head[u],c}; head[u]=maxedge; edge[++maxedge]=(Edge){u,head[v],c}; head[v]=maxedge; } struct Road { int from,to; int cost; bool operator < (const Road t) const { return cost>t.cost; } }road[maxm]; int n,m; inline void init() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].cost); for(int i=1;i<=n;i++) fa[i]=i; memset(head,-1,sizeof(head)); maxedge=-1; } void kruskal() { sort(road+1,road+m+1); int pos=1,tot=0; while(pos<=m && tot^(n-1)) { if(union_find(road[pos].from,road[pos].to)) tot++,addedge(road[pos].from,road[pos].to,road[pos].cost); pos++; } } int f[maxn][maxd+5],dp[maxn][maxd+5]; // the node 2^j after u int depth[maxn]; void dfs(int u,int deep) { depth[u]=deep; for(int k=1;(1<<k)<=n;k++) { f[u][k] = f[f[u][k-1]][k-1]; dp[u][k] = min(dp[u][k-1] , dp[f[u][k-1]][k-1]); } for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(!depth[v]) { f[v][0]=u;dp[v][0]=edge[i].val;//initialize here rather than in the former context!! no considering the roor coz INF is INF, not changing and not visiting the value!! dfs(v,deep+1); } } } int Find(int u,int v) { int ans=INF; if(depth[u] < depth[v]) swap(u,v);// making the u is deeper!! // make u v at the same depth for(int k=maxd;k>=0;k--) // k>=0 here!! or cannot jump at the same depth!!! if(depth[v] <= depth[f[u][k]]) // f[0] = 0, indicates its beyond the root!! { smin( ans , dp[u][k] ); u=f[u][k]; } if(u == v) return ans; //special judge of one of them is the LCA // jump at the same time for(int k=maxd;k>=0;k--) // k>=0 here too!! if(f[u][k] ^ f[v][k]) { smin(ans , min( dp[u][k] , dp[v][k] ) ); u = f[u][k]; v = f[v][k]; } return min( ans , min(dp[u][0] , dp[v][0])); // u v this time is the F1 of LCA } int main() { freopen("truck.in","r",stdin); freopen("truck.out","w",stdout); init(); kruskal(); memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) if(!depth[i]) dfs(i,1); int q; scanf("%d",&q); while(q--) { int x,y; scanf("%d%d",&x,&y); if(find(x)^find(y)) printf("-1 "); else printf("%d ",Find(x,y)); } return 0; }
然后的话粘一发 LCA 裸题 HDU 2874
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; const int maxn=10005; struct Edge { int to,next; int val; }edge[maxn*maxn]; int head[maxn]; int maxedge; inline void addedge(int u,int v,int c) { edge[++maxedge]=(Edge){v,head[u],c}; head[u]=maxedge; edge[++maxedge]=(Edge){u,head[v],c}; head[v]=maxedge; } int n,m,q; int fa[maxn]; int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } bool union_find(int x,int y) { int t1=find(x),t2=find(y); if(t1==t2) return false; fa[t2]=t1; return true; } int maxnode; int dfn[maxn],ver[maxn<<1];//dfn: first visit maxnode, ver: reverse function of dfn, indicating the number of vertex int depth[maxn<<1],dis[maxn];//depth: the depth of dfn, dis: from root to vertex inline bool init() { if(!~scanf("%d%d%d",&n,&m,&q)) return false; for(int i=1;i<=n;i++) fa[i]=i; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); maxedge=-1;maxnode=0; for(int i=1;i<=m;i++) { int u,v,c; scanf("%d%d%d",&u,&v,&c); addedge(u,v,c); union_find(u,v); } return true; } void dfs(int u,int deep) { dfn[u]=++maxnode;ver[maxnode]=u;depth[maxnode]=deep; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(dfn[v]) continue; dis[v]=dis[u]+edge[i].val; dfs(v,deep+1); depth[++maxnode]=deep;ver[maxnode]=u; } } const int maxdepth=20; int dp[maxn<<1][maxdepth]; void ST(int n)// n=::maxnode { for(int i=1;i<=n;i++) dp[i][0]=i; for(int j=1;(1<<j)<=n;j++)//careful of the limits for(int i=1;i+(1<<j)-1<=n;i++)//careful of the limits by y=dp[i+(1<<j-1)][j-1] and the limit of i+(1<<j)-1<=n, must -1 'coz the i+(1<<j)-1 is possible to be n!! { int x=dp[i][j-1],y=dp[i+(1<<j-1)][j-1]; dp[i][j]=depth[x]<depth[y]?x:y; } } inline int RMQ(int l,int r) { int k=0; while(1<<(k+1)<=r-l+1) k++;// careful of the limits!! int x=dp[l][k],y=dp[r-(1<<k)+1][k]; return depth[x]<depth[y]?x:y; } inline int LCA(int u,int v) { int x=dfn[u],y=dfn[v]; if(x>y) swap(x,y); int root=RMQ(x,y); return ver[root]; } int main() { freopen("city.in","r",stdin); freopen("city.out","w",stdout); while(init()) { for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i,1); ST(maxnode); while(q--) { int x,y; scanf("%d%d",&x,&y); if(find(x)^find(y)) printf("Not connected "); else printf("%d ",dis[x]+dis[y]-(dis[LCA(x,y)]<<1)); } } return 0; }