zoukankan      html  css  js  c++  java
  • CF1396E Distance Matching

    一、题目

    点此看题

    二、解法

    技巧性极强的构造题,惜吾构造而不终也,思路大体有了,但还差点火候

    首先考虑合法的必要条件,我们先考察边权的最大值和最小值来得到大体的范围。我们考虑每条边的贡献,边 ((u,v)) 断开后形成的子树大小是 (siz[u],siz[v]),可以得到上下界分别是:

    [siz[u]\%2leq wleqmin(siz[u],siz[v]) ]

    为了去掉 (min),我们取中心为根,那么现在贡献上界是 (sum_{u ot=rt}siz[u]),下界是 (sum_{u}siz[u]\%2)

    继续考虑必要条件,因为 (dis(x,y)=dep[x]+dep[y]-2dep[lca]),更换点的匹配只会引起 (lca) 这一项的变化,所以无论如何边权和的奇偶性不变

    所以必要条件是:(mileq kleq mx,k=mxmod 2),现在考虑给出它的充分性证明,直接上构造:

    考虑从边权和为 (mx) 的方案调整到为 (k) 的方案,一开始可以把 ( t dfn) 序为 (i) 的点和 ( t dfn) 序为 (i+n/2) 的点配对,这样是能构造到 (mx) 的(下文称之为初方案),而且所有路径都跨过重心,我们在构造时考虑维护根为重心的这个性质,所以我们选取点数最大的子树来操作,设 (y) 表示最深的非叶节点:

    • 如果 (2dep[y]<mx-k),我们直接拿 (y) 配对(优先儿子,可以自身),那么配对后相较于初方案的边权和会减少 (2dep[y]),配对后删除这两个点,树的形态不变,我们在新树上构造出新的初方案。
    • 如果 (2dep[y]geq mx-k),因为 (dep) 连续这一特点,我们找到 (dep[z]=frac{mx-k}{2}),然后类似于上述方案配对即可,边权和会减少 (2dep[z]),这说明我们找到了答案。

    如果还有未匹配的点就按初方案来构造即可,用 ( t set) 轻松维护,时间复杂度 (O(nlog n))

    三、总结

    构造出某一特殊取值的题可以考虑上下界,然后从某一边界开始调整。

    树上路径和问题可以使用贡献法,考虑边的贡献。

    如果树上遇到 (min(..siz..)) 之类的限制,可以通过取重心为根的方式来去除。

    #include <cstdio>
    #include <cassert>
    #include <iostream>
    #include <set>
    using namespace std;
    const int M = 100005;
    #define int long long
    #define pii pair<int,int>
    #define mp make_pair
    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,tot,rt,f[M],cur[M],cl[M],siz[M],mx[M],fa[M],dep[M];
    set<pii> s,b[M];int m,k,rs,mi,dfn[M],use[M],out[M];
    struct edge
    {
    	int v,next;
    }e[2*M];
    void dfs1(int u,int fa)
    {
    	siz[u]=1;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa) continue;
    		dfs1(v,u);
    		siz[u]+=siz[v];
    		mx[u]=max(mx[u],siz[v]);
    	}
    	mx[u]=max(mx[u],n-siz[u]);
    	if(mx[u]<mx[rt] || !rt) rt=u;
    }
    void dfs2(int u,int p,int rt)
    {
    	siz[u]=1;cl[u]=rt;
    	fa[u]=p;dep[u]=dep[p]+1;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==p) continue;
    		out[u]++;
    		dfs2(v,u,rt);
    		siz[u]+=siz[v];
    	}
    	mi+=siz[u]%2;rs+=siz[u];
    	if(out[u]) b[rt].insert(mp(dep[u],u));
    }
    void dfs3(int u)
    {
    	if(!use[u]) dfn[++m]=u;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa[u]) continue;
    		dfs3(v);
    	}
    }
    void del(int u)
    {
    	int x=fa[u];
    	out[x]--;use[u]=1;
    	if(out[x]==0) b[cl[x]].erase(mp(dep[x],x));
    }
    void match(int u)
    {
    	int t=0,p[5]={};
    	for(int &i=cur[u];i && t<2;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(!use[v] && v!=fa[u]) p[++t]=v;
    	}
    	if(!use[u]) p[++t]=u;
    	assert(t>=2);
    	printf("%lld %lld
    ",p[1],p[2]);
    	del(p[1]);del(p[2]);
    }
    signed main()
    {
    	n=read();k=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		e[++tot]=edge{v,f[u]},f[u]=tot;
    		e[++tot]=edge{u,f[v]},f[v]=tot;
    	}
    	for(int i=1;i<=n;i++) cur[i]=f[i];
    	dfs1(1,0);//find root
    	for(int i=f[rt];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		dfs2(v,rt,v);//get the dep
    		if(siz[v]>1) s.insert(mp(siz[v],v));
    	}
    	if(mi>k || rs<k || rs%2!=k%2)
    	{
    		puts("NO");
    		return 0;
    	}
    	puts("YES");
    	rs=rs-k;
    	while(rs)
    	{
    		int x=s.rbegin()->second,y=b[x].rbegin()->second;
    		s.erase(mp(siz[x],x));
    		if(2*dep[y]>=rs)//we can get the answer
    		{
    			y=b[x].lower_bound(mp(rs/2,0))->second;
    			match(y);break;
    		}
    		rs-=2*dep[y];siz[x]-=2;
    		match(y);
    		if(siz[x]>1) s.insert(mp(siz[x],x));
    	}
    	dfs3(rt);
    	assert(m%2==0);
    	for(int i=1;i<=m/2;i++)
    		printf("%lld %lld
    ",dfn[i],dfn[i+m/2]);
    }
    
  • 相关阅读:
    Android环境说明 与 屏幕尺寸问题
    关于修改Visual Studio 2010 Ultimate RC的密钥
    安装Office 2010是出现windows installer服务不能更新一个或多个受保护的windows文件 错误
    Sharepoint 2010和阿里通进行集成完成短信通知功能
    用visual studio 2010 打开winform程序 时 无法运行 的解决方案
    一次尴尬的招聘经历
    TFS里的MSB3021错误
    程序员是强者
    修改密码导致应用程序池无法启动
    远离客户开发陷阱(转)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15440762.html
Copyright © 2011-2022 走看看