CF526G Spiders Evil Plan


一、题目

点此看题

二、解法

网上的很多题解讲的都不清楚,我还是尽量不要避重就轻??

首先不考虑连通块包含 \(x\) 的限制,考虑一个经典结论,对于一个具有 \(k\) 个叶子的无根树,我们可以构造出使用 \(\lceil\frac{k}{2}\rceil\) 条路径覆盖完树所有边的方案,并且这个构造一定是下界。

那么一个连通块合法的条件就是其叶子数 \(\leq 2y\),我们考虑用叶子集合来表示连通块,那么选出叶子之后它们的极小连通块(也就是点数最少的导出子图,使得保留这些点之后叶子联通)就是原问题中的连通块。

然后有一个 \(\tt observation\):在根被选取的前提下,如果 \(u\) 的子树有点被选了,那么 \(u\) 的长链一定会出现在连通块中。这是因为必然存在一条根到子树内的路径出现在连通块中,那么长链肯定在最优方案中。

如果我们先枚举根,这又是一个经典问题,我们把长链的贡献记在链顶上,那么从大到小依次选取即可。其实这就是 BZOJ攻略,证明的话可以考虑建出网络流模型,然后不会发生退流操作。

因为直径的某个端点一定作为叶子被选取了,所以我们可以以直径的某个端点为根计算答案,然后取最大值即可。

最后考虑包含 \(x\) 的限制,如果前 \(2y-1\) 条长链已经包含 \(x\) 是最好的。否则我们考虑调整法,先加入前 \(2y-2\) 条长链,然后调整的方案是这两种情况之一:

  • 最后一条长链不选,转而选取 \(x\) 所在的长链。
  • 最后一条长链选,我们找到离 \(x\) 最近的长链,把它最下面一段替换成 \(x\) 所在的长链。

可以简单的用倍增实现(把长链排名放在点上),时间复杂度 \(O(n\log n)\)

三、总结

懂得套用一些经典结论来思考图论问题。

边权和贡献最大问题可以往长链剖分贪心这个角度考虑。

#include 
#include 
#include 
#include 
using namespace std;
const int M = 100005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,tot,zxy,ans,f[M];
struct edge {int v,c,next;}e[M<<1];
struct graph
{

int rt,fa[M][20],g[M][20],dep[M];
int k,son[M],len[M],top[M],sum[M],rnk[M];
void find(int u,int fa)
{
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dep[v]=dep[u]+e[i].c;
		find(v,u);
	}
}
void dfs(int u,int p)
{
	for(int i=1;i<20;i++)
	{
		fa[u][i]=fa[fa[u][i-1]][i-1];
		g[u][i]=g[u][i-1]+g[fa[u][i-1]][i-1];
	}
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v,c=e[i].c;
		if(v==p) continue;
		fa[v][0]=u;g[v][0]=c;
		dfs(v,u);
		if(dep[v]+c>dep[u])
			dep[u]=dep[v]+c,son[u]=v;
	}
	for(int i=f[u];i;i=e[i].next)
		if(e[i].v^p && e[i].v^son[u])
			top[++k]=e[i].v,
			len[e[i].v]=dep[e[i].v]+e[i].c;
}
void init(int s)
{
	find(s,0);rt=s;
	for(int i=1;i<=n;i++)
		if(dep[i]>dep[rt]) rt=i;
	memset(dep,0,sizeof dep);
	dfs(rt,0);top[++k]=rt;len[rt]=dep[rt];
	sort(top+1,top+1+k,[&](int i,int j)
	{return len[i]>len[j];});
	for(int i=1;i<=k;i++)
		sum[i]=sum[i-1]+len[top[i]];
	for(int i=1;i<=k;i++)
	{
		int x=top[i];
		while(x) rnk[x]=i,x=son[x];
	}
}
int ask1(int x,int y)
{
	int res=dep[x];
	for(int i=19;i>=0;i--)
		if(rnk[fa[x][i]]>=y)
			res+=g[x][i],x=fa[x][i];
	return sum[y-1]+res+g[x][0];
}
int ask2(int x,int y)
{
	int res=dep[x];
	for(int i=19;i>=0;i--)
		if(rnk[fa[x][i]]>y)
			res+=g[x][i],x=fa[x][i];
	return sum[y]+res+g[x][0]-dep[fa[x][0]];
}
int qry(int x,int y)
{
	y=2*y-1;
	if(y>=k) return zxy;
	return rnk[x]<=y?sum[y]:
	max(ask1(x,y),ask2(x,y));
}

}T1,T2; 
signed main()
{
	n=read();m=read();
	for(int i=1;i