luogu P4103 [HEOI2014]大工程 虚树 + 未调完

本文深入探讨了图论算法中的关键概念和技术,包括深度优先搜索、最近公共祖先(LCA)算法、离线查询处理以及动态规划在树上的应用。通过详细的代码实现和案例分析,为读者提供了全面的理解和实践指南。

Code: 

#include<bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin) // , freopen(s".out","w",stdout)  
#define maxn 1000001 
#define inf 1000000000
#define ll long long   
using namespace std;          
vector<int>G[maxn];    
int edges,tim,root,top;                
int hd[maxn], to[maxn<<1], val[maxn<<1], nex[maxn<<1];  
int dep[maxn],Top[maxn],hson[maxn],siz[maxn],dfn[maxn],fa[maxn],arr[maxn],S[maxn],mk[maxn];   
inline void add(int u,int v)
{
	nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=1; 
}  
void dfs1(int u,int ff)
{
	fa[u]=ff,siz[u]=1,dep[u]=dep[ff]+1,dfn[u]=++tim; 
	for(int i=hd[u];i;i=nex[i])
	{ 
		int v=to[i];
		if(v==ff) continue; 
		dfs1(v,u); 
		siz[u]+=siz[v]; 
		if(siz[v]>siz[hson[u]]) hson[u]=v; 
	} 
}
void dfs2(int u,int tp)
{
	Top[u]=tp; 
	if(hson[u]) dfs2(hson[u],tp); 
	for(int i=hd[u];i;i=nex[i])
	{
		int v=to[i];
		if(v==fa[u]||v==hson[u]) continue; 
		dfs2(v,v); 
	}
}
inline int LCA(int x,int y)
{
	while(Top[x]!=Top[y]) 
	{ 
		dep[Top[x]] > dep[Top[y]] ? x = fa[Top[x]] : y = fa[Top[y]]; 
	}
	return dep[x] < dep[y] ? x : y;  
}
inline int getdis(int x,int y)
{
	return dep[x] + dep[y] - (dep[LCA(x,y)] << 1); 
}
inline void addvir(int u,int v)
{
	G[u].push_back(v); 
}
void insert(int x)
{
	if(top<=1) { S[++top]=x; return;  }
	int lca=LCA(x, S[top]); 
	if(lca == S[top]) { S[++top] = x; return; }
	while(top > 1 && dep[S[top - 1]] >= dep[lca]) addvir(S[top - 1], S[top]), --top; 
	if(lca != S[top]) addvir(lca, S[top]), S[top] = lca;      
	S[++top] = x;   
}             
bool cmp(int i,int j)
{
	return dfn[i] < dfn[j]; 
}     
ll ans=0,a1,a2; 
int size[maxn],d1[maxn],d2[maxn],dmin[maxn],k; 
void DP(int x) 
{     
	size[x]=mk[x];      
	d1[x]=d2[x]=0, dmin[x]=inf;  
	for(int i=0;i<G[x].size();++i)
	{
		int v = G[x][i],w = getdis(x, G[x][i]);    
		DP(v);     
		if(mk[v]) dmin[x]=min(dmin[x],w);
		else dmin[x]=min(dmin[x], w+dmin[v]);  
		int curd=w+d1[v]; 
		if(curd > d1[x]) d2[x]=d1[x], d1[x]=curd; 
		else if(curd > d2[x]) d2[x] = curd;     
		ans+=1ll*size[v]*w*(k-size[v]),size[x]+=size[v];        
	}   
	a1=max(a1, 1ll*(d1[x] + d2[x]));   
	if(mk[x]) a2=min(a2, 1ll*dmin[x]);    
	G[x].clear();       
}
inline void work() 
{ 
	scanf("%d",&k);  
	for(int i=1;i<=k;++i) scanf("%d",&arr[i]), mk[arr[i]] = 1; 
	sort(arr+1,arr+1+k,cmp);     
    S[top=0]=root=ans=0; 
    for(int i=1;i<=k;++i)   insert(arr[i]);         
    for(int i=1;i<=top;++i) if(dep[S[i]] < dep[root] || !root) root = S[i]; 
    while(top > 1) addvir(S[top-1], S[top]),--top; 
    a1=-inf, a2=inf, DP(root);
    printf("%lld %lld %lld\n",ans,a2,a1);    
    for(int i=1;i<=top;++i) mk[arr[i]]=0;                 
}        
int main()
{
	// setIO("input");    
	int n; 
	scanf("%d",&n); 
	for(int i=1;i<n;++i) 
	{
		int a,b; 
		scanf("%d%d",&a,&b); 
		add(a,b), add(b,a); 
	}  
	dep[1]=1,dfs1(1,0),dfs2(1,1);  
	int Q; 
	scanf("%d",&Q);   
	while(Q--)  work(); 
	return 0; 
}

  

洛谷P1177是【模板】排序题,可使用归并排序来解决。归并排序的核心思想是分治法,即将一个问题分解为多个小问题,分别解决后再合并结果。 归并排序主要步骤如下: 1. **分解**:将待排序数组从中间分成两个子数组,递归地对这两个子数组进行排序。 2. **合并**:将两个已排序的子数组合并成一个有序数组。 以下是使用归并排序解决洛谷P1177题目的代码实现: ```cpp #include<bits/stdc++.h> #include<iomanip> using namespace std; #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) const int MAXN = 1e5 + 5; int a[MAXN], b[MAXN]; int n; // 数组长度 // 合并两个已排序的子数组 void mergesort(int l1, int r1, int l2, int r2) { int i = l1, j = l2, k = l1; while (i <= r1 && j <= r2) { if (a[i] <= a[j]) { b[k++] = a[i++]; } else { b[k++] = a[j++]; } } while (i <= r1) b[k++] = a[i++]; while (j <= r2) b[k++] = a[j++]; for (i = l1; i <= r2; i++) { a[i] = b[i]; } } // 递归进行归并排序 void merge(int l, int r) { if (l >= r) { return; } int mid = (l + r) / 2; merge(l, mid); merge(mid + 1, r); mergesort(l, mid, mid + 1, r); } int main() { IOS; cin >> n; for (int i = 0; i < n; i++) { cin >> a[i]; } merge(0, n - 1); for (int i = 0; i < n; i++) { cout << a[i]; if (i < n - 1) cout << " "; } cout << endl; return 0; } ``` 上述代码中,`merge`函数用于递归地将数组分解为子数组,`mergesort`函数用于合并两个已排序的子数组。在`main`函数中,首先读取输入的数组,然后用`merge`函数进行排序,最后输出排序后的数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值