zoukankan      html  css  js  c++  java
  • 「CEOI2020」春季大扫除 题解

    题意

    (~~~~) 给出若干次询问,每次询问将对若干个点增加叶子结点(同一个点可能加多次),定义一组叶子结点匹配的权值为这两点间路径经过的边数,求使得匹配权值最小且每条边都至少贡献过一次的权值。
    (~~~~) (1leq nleq 10^5,1leq sum D_i leq 10^5)

    本文版权归 Azazel 与博客园共有,欢迎转载,但需保留此声明,并给出原文地址,谢谢合作。

    原文地址:https://www.cnblogs.com/Azazel/p/15192326.html

    题解

    (~~~~) 首先,叶子结点必须是偶数时才有解,因为奇数时至少会存在一个叶子其到父亲的边不能被清扫。

    (~~~~) 接下来考虑一棵子树:

    • 当子树内叶子结点为奇数时,该子树根节点到父亲的边会被清扫一次;
    • 当子树内叶子结点为偶数时,该子树根节点到父亲的边会被清扫两次;

    (~~~~) 所以题解几乎写题意里了属于是

    (~~~~) 考虑证明这个结论的正确性:每个点(除去钦定的根节点)到父亲的边都应该被清扫至少一次。当子树内只剩一个叶子结点未匹配时,外界也会有奇数个叶子结点供该点匹配,偶数同理。当每个点(除去钦定的根节点)到父亲的边都被清扫,我们就可以认为整棵树被清扫了。

    (~~~~) 以上是一个 (mathcal{o(nq)}) 的做法,考虑我们需要的信息只有每个点子树内的叶子结点数量 (mod 2),故我们用树剖维护,对于某个点若其为叶子结点则不管,否则将该点到根节点的路径上所有点的点权 (oplus 1) ,最后统计除了 (1) 之外所有点的点权和。由于 (1) 的点权必定为 (0),所以统计整棵树的点权和即可。

    代码

    查看代码
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    int n,q,m;
    int siz[100005],dfn[100005],top[100005],sizLeaf[100005];
    int dep[100005],fa[100005],To[100005],Times,son[100005],deg[100005];
    bool IsLeaf[100005];
    vector <int> G[100005];
    void dfs1(int u,int Fa)
    {
    	sizLeaf[u]=IsLeaf[u];fa[u]=Fa;
    	siz[u]=1;dep[u]=dep[Fa]+1;
    	for(int i=0;i<G[u].size();i++)
    	{
    		int v=G[u][i];
    		if(v==Fa) continue;
    		dfs1(v,u);
    		siz[u]+=siz[v];sizLeaf[u]=(sizLeaf[u]+sizLeaf[v])%2;
    		if(siz[v]>siz[son[u]]) son[u]=v;
    	}
    }
    void dfs2(int u,int T)
    {
    	top[u]=T;dfn[u]=++Times;To[Times]=u;
    	if(!son[u]) return; dfs2(son[u],T);
    	for(int i=0;i<G[u].size();i++)
    	{
    		int v=G[u][i];
    		if(v==fa[u]||v==son[u]) continue;
    		dfs2(v,v);
    	}
    }
    struct SegmentTree{
    	#define ls p<<1
    	#define rs p<<1|1
    	#define lson p<<1,l,mid
    	#define rson p<<1|1,mid+1,r
    	int tr[400005],tag[400005];
    	void pushUp(int p){tr[p]=tr[ls]+tr[rs];}
    	void pushDown(int p,int l,int r)
    	{
    		if(tag[p])
    		{
    			int mid=(l+r)>>1;
    			tr[ls]=(mid-l+1)-tr[ls];
    			tr[rs]=(r-mid)-tr[rs];
    			tag[ls]^=1; tag[rs]^=1;
    			tag[p]^=1;
    		}
    	}
    	void Build(int p,int l,int r)
    	{
    		if(l==r)
    		{
    			tr[p]=sizLeaf[To[l]];
    			return;
    		}
    		int mid=(l+r)>>1;
    		Build(lson); Build(rson);
    		pushUp(p);
    	}
    	void Modify(int p,int l,int r,int lx,int rx)
    	{
    		if(lx<=l&&r<=rx)
    		{
    			tr[p]=(r-l+1)-tr[p];
    			tag[p]^=1;
    			return;
    		}
    		int mid=(l+r)>>1;pushDown(p,l,r);
    		if(lx<=mid) Modify(lson,lx,rx);
    		if(mid<rx)  Modify(rson,lx,rx);
    		pushUp(p);
    	}
    	int Query(int p,int l,int r,int lx,int rx)
    	{
    		if(lx<=l&&r<=rx) return tr[p];
    		int mid=(l+r)>>1,ret=0;pushDown(p,l,r);
    		if(lx<=mid) ret+=Query(lson,lx,rx);
    		if(mid<rx)  ret+=Query(rson,lx,rx);
    		return ret;
    	}
    }Seg;
    void ModifyPath(int x,int y)
    {
    //	printf("%d %d:",x,y);
    	while(top[x]!=top[y])
    	{
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
    		Seg.Modify(1,1,n,dfn[top[x]],dfn[x]);
    		x=fa[top[x]];
    	}
    	if(dep[x]>dep[y]) swap(x,y);
    	Seg.Modify(1,1,n,dfn[x],dfn[y]);
    //	for(int i=1;i<=n;i++) printf("%d ",Seg.Query(1,1,n,dfn[i],dfn[i]));puts("");
    }
    vector <int> Changed,V;
    int main() {
    //	freopen("1.in","r",stdin);
    //	freopen("1.out","w",stdout);
    	scanf("%d %d",&n,&q);
    	for(int i=1,x,y;i<n;i++)
    	{
    		scanf("%d %d",&x,&y);
    		G[x].push_back(y);
    		G[y].push_back(x);
    		deg[x]++;deg[y]++;
    	}
    	int Sum=0,Tmp;
    	for(int i=1;i<=n;i++) if(deg[i]==1) IsLeaf[i]=true,Sum++; 
    	dfs1(1,0);dfs2(1,1);
    	Seg.Build(1,1,n);
    //	for(int i=1;i<=n;i++) printf("%d ",Seg.Query(1,1,n,dfn[i],dfn[i]));puts("");
    	Tmp=Sum;
    	while(q--)
    	{
    		Sum=Tmp;
    		scanf("%d",&m);
    		for(int i=1,x;i<=m;i++)
    		{
    			scanf("%d",&x);
    			if(IsLeaf[x])
    			{
    				Changed.push_back(x);
    				IsLeaf[x]=false;
    				continue;
    			}
    			V.push_back(x);Sum++;
    			ModifyPath(x,1);
    		}
    		if(Sum&1) puts("-1");
    		else printf("%d
    ",((n+m)<<1)-2-(Seg.tr[1])-m);
    		for(int i=0;i<V.size();i++) ModifyPath(V[i],1);
    		for(int i=0;i<Changed.size();i++) IsLeaf[Changed[i]]=true;
    		V.clear();Changed.clear();
    	}
    	return 0;
    }
    
  • 相关阅读:
    pku 1077 Eight
    poj 1700 过河问题 贪心法
    字典树
    [转] 解读IntelliJ IDEA的优缺点
    【转】STL 容器类内部实现
    Google Chrome太强大了
    【转】从哈希存储到Bloom Filter
    [转]我的多年羽毛球自学心得
    好书推荐
    【转】C++错误中英文对照表
  • 原文地址:https://www.cnblogs.com/Azazel/p/15192326.html
Copyright © 2011-2022 走看看