传送门:QAQQAQ题面翻译

以后博客可能一直咕咕咕了。一些做题的思考可能会直接放在代码里而不是单独写博客,因为这样太浪费时间,只有一些比较新的题才会单独写博客

思路:对于这种构造可行解使得权值和恰好为某一值的题,一般都是先求出可以构造出来的最大和最小值,然后从某个极值按照一定方法进行连续修改

我们考虑每一条边对于答案的贡献:若边$E(u,v)$把数分成$U,V$两颗子树,则该边最大的贡献是$min(sz[U],sz[V])$,最小是$sz[U]\mod2$。

$min$太难处理,所以想一种办法把$min$去掉,通过重心的性质(每一个子树的$size$小于等于$\frac{n}{2}$),发现直接取重心就可以把$min$去掉了

所以就可以得到

$$minans=\sum_{i=1}^{n} [i \neq root]sz[i]\mod2$$

$$maxans=\sum_{i=1}^{n} [i \neq root]sz[i]$$

然后仔细想想会发现:答案有解的充要条件是$minans \leq k \leq maxans$且$(maxans-k)\mod2=0$

充分性:通过构造方法证明。

  每次取在$size$最大的子树中选取两个$lca$深度最大的点,因为本来两个点都是向字树外连的,现在自己相连,所以$\Delta =2*dep[lca]$,然后删掉那两个点

  容易发现这样构造是必定可以从$maxans$变成$minans$的,因为对于$sz[u]\mod2=0$的边,它底下肯定两两配对;对于$sz[u]\mod2=0$,这样的贪心会使得下面只有一个点向上经过它

  因为点数始终为偶数,所以从最大子树删掉两个点以后不会使得次大子树的$size$大于$\frac{n}{2}$,上面求$minans,maxans$的式子始终成立

  当最后一次$dep[lca]*2>rest$时,因为所有不是叶子节点的点都可以作$lca$,所以$dep$必定连续,即肯定能找到构造出刚好使得$rest=0$的方案。

  剩下的点按照$max$的方案,跨子树分别连就可以了

必要性:因为$\Delta =2*dep[lca]$,所以如果不是$k,maxans$不是同奇同偶,一定无解

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=120000;
typedef long long ll;
typedef pair<int,int> pii;
#define mk make_pair
int n; ll k; vector<int> v[N];
int sz[N],dep[N],fa[N],root; ll minn=N,maxn=0;
void dfs1(int u,int f)
{
sz[u]=1; int maxsz=0;
for(int i=0;i<(int)v[u].size();i++)
{
int to=v[u][i]; if(to==f) continue;
dfs1(to,u); sz[u]+=sz[to];
maxsz=max(maxsz,sz[to]);
}
if(minn>max(maxsz,n-sz[u])) minn=max(maxsz,n-sz[u]),root=u;
} int top[N],deg[N];
set<pii> S[N],R;//S维护子树中所有可能作为lca的点(即不是叶子)
void dfs(int u,int from)
{
sz[u]=1;
top[u]=from;
if(from&&(int)v[u].size()-1>=1) S[from].insert(mk(dep[u],u));
for(int i=0;i<(int)v[u].size();i++)
{
int to=v[u][i]; if(to==fa[u]) continue;
if(u==root) from=to; deg[u]++;
dep[to]=dep[u]+1; fa[to]=u;
dfs(to,from); sz[u]+=sz[to];
}
} void del(int x)
{
if(!--deg[fa[x]])
S[top[x]].erase(mk(dep[fa[x]],fa[x]));
} int vis[N];
vector<int> rem;
void dfs3(int u)
{
if(!vis[u]) rem.push_back(u);
for(int i=0;i<(int)v[u].size();i++)
{
int to=v[u][i]; if(to==fa[u]) continue;
dfs3(to);
}
} int main()
{
scanf("%d%lld",&n,&k);
for(int i=1;i<n;i++)
{
int x,y; scanf("%d%d",&x,&y);
v[x].push_back(y); v[y].push_back(x);
}
dfs1(1,-1); dep[root]=0; dfs(root,0);
minn=0,maxn=0;
for(int i=1;i<=n;i++)
if(i!=root) maxn+=sz[i], minn+=sz[i]%2;
if(k>maxn||k<minn||(maxn-k)&1) {puts("NO"); return 0;}
puts("YES");
for(int i=0;i<v[root].size();i++)
{
int to=v[root][i];
if(sz[to]>1) R.insert(mk(sz[to],to));
}
ll rest=maxn-k;
while(rest)
{
int now=R.rbegin()->second; R.erase(--R.end());
int pos=S[now].rbegin()->second;
if(2*dep[pos]>rest)
{
rest/=2;
pos=S[now].lower_bound(mk(rest,0))->second;
vector<int> V; V.clear();
for(int i=0;i<(int)v[pos].size();i++)
{
int to=v[pos][i];
if(to==fa[pos]||vis[to]) continue;
V.push_back(to);
}
if((int)V.size()<2) V.push_back(pos);
printf("%d %d\n",V[0],V[1]); vis[V[0]]=1; vis[V[1]]=1;
rest-=dep[pos];
break;
}
else
{
vector<int> V; V.clear();
for(int i=0;i<(int)v[pos].size();i++)
{
int to=v[pos][i];
if(to==fa[pos]||vis[to]) continue;
V.push_back(to);
}
if((int)V.size()<2) V.push_back(pos); rest-=dep[pos]*2;
printf("%d %d\n",V[0],V[1]); vis[V[0]]=1; vis[V[1]]=1;
del(V[0]); del(V[1]);
}
sz[now]-=2;
if(sz[now]>1) R.insert(mk(sz[now],now));
}
dfs3(root);
int T=(int)rem.size()/2;
for(int i=0;i<T;i++) printf("%d %d\n",rem[i],rem[i+T]);
return 0;
}

CF1396E——Distance Matching的更多相关文章

  1. [cf1396E]Distance Matching

    根据$dis(x,y)=d[x]+d[y]-2d[lca(x,y)]$,由于所有点都出现了1次,距离即$\sum_{i=1}^{n}d_{i}-2\sum d[lca(x,y)]$(以下假设根深度为0 ...

  2. [atARC087F]Squirrel Migration

    对这棵树重心情况分类讨论: 1.若这棵树存在两个重心,分别记作$x$和$y$,如果将$(x,y)$断开,两棵子树大小都相同(都为$\frac{n}{2}$),此时$p_{i}$与$i$必然不同属于一个 ...

  3. world.construct(me);

    目录 0 引言 0.1 所谓构造题 0.2 重点是动机 (motivation) 1 实践出真知 1.1 「CSP-S 2021」「洛谷 P7915」回文 1.1.1 题目大意 1.1.2 解题过程 ...

  4. 最喜欢的算法(们) - Levenshtein distance

    String Matching: Levenshtein distance Purpose: to use as little effort to convert one string into th ...

  5. Scipy教程 - 距离计算库scipy.spatial.distance

    http://blog.csdn.net/pipisorry/article/details/48814183 在scipy.spatial中最重要的模块应该就是距离计算模块distance了. fr ...

  6. Direct Shot Correspondence Matching

    一篇BMVC18的论文,关于semantic keypoints matching.dense matching的工作,感觉比纯patch matching有意思,记录一下. 1. 摘要 提出一种针对 ...

  7. Matching Networks for One Shot Learning

    1. Introduction In this work, inspired by metric learning based on deep neural features and memory a ...

  8. Hausdorff Distance(豪斯多夫距离)

    Hausdorff Distance(豪斯多夫距离) 参考博客:http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/98/normand/ma ...

  9. Minimum edit distance(levenshtein distance)(最小编辑距离)初探

    最小编辑距离的定义:编辑距离(Edit Distance),又称Levenshtein距离.是指两个字串之间,由一个转成还有一个所需的最少编辑操作次数.许可的编辑操作包含将一个字符替换成还有一个字符. ...

随机推荐

  1. “工程师思维” VS. “学院派思维”

    1.与"工程师"交流,他们致力于"更快.高质量"交付,他们会借助时下最稳定.最完善的中间件或者框架,他们更谦虚,喜欢和志同道合的朋友交流分享协作,视角更宽,往往 ...

  2. Iptables 下 SNAT、DNAT和MASQUERADE三者之间的区别

    Iptables 中可以灵活的做各种网络地址转换(NAT,Network Address Translation) 网络地址转换(NAT)主要有两种:SNAT 和 DNAT,但是也有一种特例 MASQ ...

  3. 创建好maven项目以后发现无法创建scala文件

    今天创建了一个maven项目  然后准备创建scala文件的时候发现没有Scala 然后只好上网上找方法了 下面是一种解决方法 1.点击file,选择settings 进去之后,选择build  进去 ...

  4. 怎么快速从产品助理/初级 PM 成长为高级 PM?

    一般想成为一枚产品经理的同学,如果没有经过系统的学习,都是从产品专员/助理开始做起的~ 那要想快速从产品助理/初级 PM 成长为高级 PM,以下这几点必不可少 直接上干货~ 全文篇幅较长,可以点赞收藏 ...

  5. c3算法

    # L(G) = [G] + [O] # G = [O] # = GO # L[E] = EO # L[F] = [F] + [GO] # F = [GO] # = FGO # L[B] = [B] ...

  6. C++ concurrent_queue

    ConcurrentQueue 用C++11提供的多线程类实现一个线程安全的队列: #include <queue> #include <mutex> #include < ...

  7. C++冷知识(1)

    func()等价于func(void) 也就是说在C++中,参数列表为空意味着不接受任何参数.之所以要注意这一点是因为在C语言中,参数列表为空意味着参数不确定.两者的语义是有巨大差别的,作为学了C再学 ...

  8. pyquery 匹配NavigableString

    pyquery 匹配NavigableString不像xpath那样精确找打匹配对象,只需匹配包含NavigableString的根节点

  9. selenium等待机制学习笔记

    转载至: https://blog.csdn.net/huilan_same/article/details/52544521 1. 强制等待 第一种也是最简单粗暴的一种办法就是强制等待sleep(x ...

  10. Spark核心组件通识概览

    在说Spark之前,笔者在这里向对Spark感兴趣的小伙伴们建议,想要了解.学习.使用好Spark,Spark的官网是一个很好的工具,几乎能满足你大部分需求.同时,建议学习一下scala语言,主要基于 ...