zoukankan      html  css  js  c++  java
  • 【CF526G】Spiders Evil Plan(贪心)

    【CF526G】Spiders Evil Plan(贪心)

    题面

    洛谷
    CodeForces
    给定一棵树,要求选择(y)条链,满足被链覆盖的所有点在树上联通,且(x)必定在联通块中。
    对于每次询问最大化被链覆盖的边的权值和。
    强制在线。

    题解

    假设我们只有一次询问,会怎么做?
    显然以(x)为根,如果(x)的度数大于(1),那么可以转化为选择(2y)个叶子节点,这样子一定存在一种方案满足链并恰好是(x)到这(2y)个节点的链的并。
    如果(x)的度数为(1)的话,显然就选择(2y-1)个点来做上述操作。
    我们发现直径的一个端点必定会被选中。
    那么我们把问题转化一下,以直径((a,b))的端点(a,b)中任意一个点为根来考虑这个问题,不妨以(a)为根来考虑。
    首先我们选择(y)条链的答案就是选择(2y-1)个叶子节点的答案。但是还需要钦定(x)在方案内。
    那么分类讨论一下,如果(x)的子树中存在一个叶子被选入了答案,那么就不用管了。
    否则,我们必须替换一个点转而选择(x)子树中的一个叶子,加入点(x)的贡献我们可以很容易的算出,现在的问题转变成了如何找到删去的最小贡献。注意这里加入(x)之后删去每个点的贡献就会改变。

    那么这样子只有两种情况,要么是删去最后一个加入答案的叶子,替换为(x)子树内的最深叶子。要么就是找到其祖先中第一个有叶子被选中的点,删去其中的一个儿子的贡献。

    维护每次选择哪个叶子的时候,可以线段树考虑,也可以长链剖分+贪心。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define mp make_pair
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,Q,lans;
    struct Line{int v,next,w;}e[MAX<<1];
    int h[MAX],cnt=1;
    inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    #define lson (now<<1)
    #define rson (now<<1|1)
    #define fr first
    #define sd second
    int mxv,rt,p[19][MAX],dfn[MAX],low[MAX],ln[MAX],md[MAX],dep[MAX],tim;
    void dfs(int u,int ff)
    {
    	p[0][u]=ff;ln[dfn[u]=++tim]=u;md[u]=dep[u];
    	for(int i=1;i<19;++i)p[i][u]=p[i-1][p[i-1][u]];
    	for(int i=h[u];i;i=e[i].next)
    	{
    		int v=e[i].v;if(v==ff)continue;
    		dep[v]=dep[u]+e[i].w;dfs(v,u);
    		md[u]=max(md[u],md[v]);
    	}
    	low[u]=tim;
    }
    pair<int,int> mx[MAX<<2];int tag[MAX<<2];
    void pushup(int now){mx[now]=max(mx[lson],mx[rson]);}
    void Build(int now,int l,int r)
    {
    	if(l==r){mx[now]=mp(dep[ln[l]],ln[l]);return;}
    	int mid=(l+r)>>1;
    	Build(lson,l,mid);Build(rson,mid+1,r);
    	pushup(now);
    }
    void Modify(int now,int l,int r,int L,int R,int w)
    {
    	if(L<=l&&r<=R){mx[now].fr+=w;tag[now]+=w;return;}
    	int mid=(l+r)>>1;
    	if(L<=mid)Modify(lson,l,mid,L,R,w);
    	if(R>mid)Modify(rson,mid+1,r,L,R,w);
    	pushup(now);mx[now].fr+=tag[now];
    }
    int ans[MAX],vis[MAX];
    void pre(int _rt)
    {
    	rt=_rt;dfs(rt,0);Build(1,1,n);
    	for(int i=2;i<=n;++i)
    	{
    		ans[i]=ans[i-1]+mx[1].fr;
    		for(int j=mx[1].sd;j&&!vis[j];j=p[0][j])
    			vis[j]=i,Modify(1,1,n,dfn[j],low[j],dep[p[0][j]]-dep[j]);
    	}
    }
    int Solve(int x,int y)
    {
    	y=min(y,n);if(vis[x]<=y)return ans[y];int u=x;
    	for(int i=18;~i;--i)if(vis[p[i][x]]>y)x=p[i][x];
    	x=p[0][x];
    	return ans[y]+md[u]-dep[x]-min(dep[x],min(ans[y]-ans[y-1],md[x]-dep[x]));
    }
    void DFS(int u,int ff,int dep)
    {
    	if(dep>mxv)mxv=dep,rt=u;
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)DFS(e[i].v,u,dep+e[i].w);
    }
    int main()
    {
    	n=read();Q=read();
    	for(int i=1,u,v,w;i<n;++i)u=read(),v=read(),w=read(),Add(u,v,w),Add(v,u,w);
    	mxv=0;DFS(1,0,0);pre(rt);
    	while(Q--)
    	{
    		int u=(read()+lans-1)%n+1,v=(read()+lans-1)%n+1;
    		printf("%d
    ",lans=Solve(u,v<<1));
    	}
    	return 0;
    }
    
  • 相关阅读:
    【redis】--安全
    【redis】-- 数据备份和恢复
    2018.2.8 cf
    寒假零碎的东西 不定时更新补充.......
    hdu 1018
    2018寒假acm训练计划
    UVAlive 7466
    母函数
    简单数学题(水的不能在水的题了)
    随便写写的搜索
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10384478.html
Copyright © 2011-2022 走看看