• 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 <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    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<n;i++)
    	{
    		int u=read(),v=read(),c=read();zxy+=c;
    		e[++tot]=edge{v,c,f[u]},f[u]=tot;
    		e[++tot]=edge{u,c,f[v]},f[v]=tot;
    	}
    	T1.init(1);T2.init(T1.rt);
    	while(m--)
    	{
    		int x=(read()+ans-1)%n+1;
    		int y=(read()+ans-1)%n+1;
    		ans=max(T1.qry(x,y),T2.qry(x,y));
    		printf("%d\n",ans);
    	}
    }
    
  • 相关阅读:
    Codeforces Round #369 (Div. 2)
    Codeforces Round #361 (Div. 2)
    【转】.NET开发人员的瓶颈和职业发展
    【资料目录收藏】.NET开发必看资料53个 经典源码77个
    IT新人养成与蘑菇理论
    软件开发技术高手转向项目管理者要突破的误区
    关于程序猿的那些笑话
    工作流管理系统的应用
    工作流管理系统的标准和产品
    工作流系统的主要组成部分
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15572977.html
Copyright © 2020-2023  润新知