Codeforces 题目传送门 & 洛谷题目传送门

%%%%% 这题也太神了吧 storz 57072 %%%%%

首先容易注意到我们选择的这 \(y\) 条路径的端点一定是叶子节点,否则我们总可以将其调整到叶子节点并使答案不会更劣,并且如果非必须(\(2y\le\) 树中叶子节点的个数),我们选择的这 \(y\) 个路径的 \(2y\) 个端点一定两两不相同,否则我们还是可以调整重复的叶子节点的位置使答案不变劣。

其次我们还可以发现,对于固定的 \(2y\) 个叶子节点,我们总存在一种选法使得这 \(y\) 个路径的并集就是这 \(2y\) 个节点的虚树,证明应该不难,大概归纳一下就行了?这里就不再赘述。特判掉 \(2y>\) 树在叶子节点个数的情况,题目就等价于求一个包含 \(x\) 的含 \(2y\) 个叶子节点的虚树,使得虚树中边权之和尽可能大。我们考虑以 \(x\) 为根,先不考虑“虚树必须包含 \(x\)”这个条件,即我们假设要求 \(x\) 与这 \(2y\) 个叶子节点共同的虚树,显然这个集合就等于这 \(2y\) 个叶子节点到根节点路径的并集,也就是说我们要选 \(2y\) 个叶子节点使得它们到根节点路径的并集中边权和尽可能大。这似乎看起来有些熟悉?……不就 BZOJ3252 攻略吗?直接长链剖分一遍排个序即可。只不过这边要特判掉 \(x\) 本身就是叶子节点的情况,因为显然 \(x\) 必须被选入这 \(2y\) 个叶子节点中,故我们还需额外选出 \(2y-1\) 个叶子节点 instead of \(2y\) 个叶子节点。加上“虚树必须包含 \(x\)”这个条件也不困难,不难发现出问题的情况只有一种可能,那就是 \(x\) 本身不是叶子节点,并且选择的 \(2y\) 的叶子节点全在 \(x\) 的同一子树中,那根据贪心的思想,我们肯定会牺牲权值最小的长链,并加入叶子节点属于 \(x\) 的其他子树中权值最大的长链,这个随便搞搞就行了,至此我们解决了单词询问的情况。

接下来考虑多组询问的情况,不难发现上述算法的瓶颈在于每次询问都要重新以 \(x\) 为根搞一遍长链剖分,而长链剖分这类数据结构也不支持可持久化什么的,因此该算法也无法直接优化。那有什么办法呢?不妨来找找性质罢,不难发现在选择的 \(2y\) 个叶节点中,总有一个最特殊的节点——那就是离 \(x\) 最远的节点,因为这个节点不论怎样也不可能被删除(每次最多删除一个节点,而我们选择的节点数 \(\ge 2\))。而根据树的直径的性质,该点一定是树的直径的一个端点,因此我们考虑着眼于树的直径上,我们对于树的直径的两个端点 \(u,v\) 分别询问以下事情:求选择 \(2y\) 个叶子节点,其中一个是 \(u\)(或 \(v\)),并且它们的虚树包含 \(x\),它们虚树边权之和的最大值。显然我们只需在两个方案中取个较大值即可。那么这个最大值怎么求呢?不妨以包含 \(u\) 的部分举例,我们还是考虑以 \(u\) 为根,显然对于固定的另外 \(2y-1\) 个叶子节点的集合 \(\{x_1,x_2,\cdots,x_{2k-1}\}\),它们与 \(u\) 的虚树也一定是这 \(2k-1\) 个节点到 \(u\) 路径的并集。如果不考虑“必须包含 \(x\)”这个条件,那依旧一遍长链剖分就可以搞定。加上这个条件怎么办呢?还是考虑先按照不必须包含 \(x\) 的方案来选择叶子节点,然后在此基础上进行调整,记 \(rk_x\) 为对于 \(x\) 节点所在长链,其链上所有边权值之和在所有长链中从大到小排名是多少。如果 \(rk_x\le 2y-1\),那 \(x\) 已经被包含入这 \(2y\) 个叶子节点的虚树中了,就不用再调整了,否则调整方法只可能有以下两种:

  1. 删去已经选择的 \(2y-1\) 个长链中,权值最小的长链(设其权值为 \(val\)),并打通 \(x\) 子树内深度最大的叶子节点 \(w\) 到根节点的路径,设 \(x\) 到根节点路径上第一个 \(rk\le 2y-2\) 的节点为 \(z\),那显然打通这条路径权值的增量为 \(dis_{w}-dis_z\),其中 \(dis_i\) 为根节点到 \(i\) 路径上权值之和,总增量为 \(\Delta=-val+dis_w-dis_z\)。
  2. 记设 \(x\) 到根节点路径上第一个 \(rk\le 2y-1\) 的节点为 \(t\),如果 \(t\) 子树中只有一个叶节点被选择,那么我们可以索性把 \(t\) 子树内原来选择的边直接去掉,取而代之的是连接 \(t\) 与 \(x\) 子树内深度最大的节点 \(w\) 的路径上的边,这部分的增量为 \(\Delta=-mxdep_t+mxdep_x\)。

求一个点到根节点上第一个 \(rk\le t\) 的点可以用倍增,总复杂度 \(n\log n\),因为有个倍增的 \(\log\)。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int LOG_N=17;
int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],val[MAXN*2+5],ec;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
namespace dia{
int dis1[MAXN+5],dis2[MAXN+5],rt1=1,rt2=1;
void dfs1(int x,int f){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
dis1[y]=dis1[x]+z;dfs1(y,x);
}
}
void dfs2(int x,int f){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
dis2[y]=dis2[x]+z;dfs2(y,x);
}
}
void find_dia(){
dfs1(1,0);for(int i=1;i<=n;i++) if(dis1[i]>dis1[rt1]) rt1=i;
dfs2(rt1,0);for(int i=1;i<=n;i++) if(dis2[i]>dis2[rt2]) rt2=i;
}
}
struct solver{
int rt,dep[MAXN+5],mxdep[MAXN+5],dson[MAXN+5],top[MAXN+5];
int fa[MAXN+5][LOG_N+2],rnk[MAXN+5];
pii chain[MAXN+5];int chain_n=0,sum[MAXN+5];
void dfs0(int x,int f){
fa[x][0]=f;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
dep[y]=mxdep[y]=dep[x]+z;dfs0(y,x);
if(mxdep[y]>mxdep[x]) mxdep[x]=mxdep[y],dson[x]=y;
}
}
void dfs1(int x,int tp){
top[x]=tp;if(dson[x]) dfs1(dson[x],tp);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==fa[x][0]||y==dson[x]) continue;
dfs1(y,y);
}
}
void init(){
dfs0(rt,0);dfs1(rt,rt);
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
for(int i=1;i<=n;i++) if(top[i]==i)
chain[++chain_n]=mp(mxdep[i]-dep[fa[i][0]],i);
sort(chain+1,chain+chain_n+1);reverse(chain+1,chain+chain_n+1);
// for(int i=1;i<=chain_n;i++) printf("%d %d\n",chain[i].fi,chain[i].se);
for(int i=1;i<=chain_n;i++) rnk[chain[i].se]=i;
for(int i=1;i<=n;i++) rnk[i]=rnk[top[i]];
for(int i=1;i<=chain_n;i++) sum[i]=sum[i-1]+chain[i].fi;
}
int getfst(int x,int rk){
for(int i=LOG_N;~i;i--) if(rnk[fa[x][i]]>rk) x=fa[x][i];
return fa[x][0];
}
int query(int x,int y){
y=(y<<1)-1;if(y>chain_n) return sum[chain_n];
if(rnk[x]<=y) return sum[y];int anc=getfst(x,y);
// printf("anc=%d\n",anc);
return max(sum[y-1]+mxdep[x]-dep[getfst(x,y-1)],sum[y]-mxdep[anc]+mxdep[x]);
}
} t[2];
int main(){
scanf("%d%d",&n,&qu);
for(int i=1,u,v,w;i<n;i++){scanf("%d%d%d",&u,&v,&w);adde(u,v,w);adde(v,u,w);}
dia::find_dia();t[0].rt=dia::rt1;t[1].rt=dia::rt2;t[0].init();t[1].init();
int ans=0;while(qu--){
int x,y;scanf("%d%d",&x,&y);x=(x+ans-1)%n+1;y=(y+ans-1)%n+1;
// printf("real %d %d\n",x,y);
printf("%d\n",ans=max(t[0].query(x,y),t[1].query(x,y)));
}
return 0;
}

Codeforces 526G - Spiders Evil Plan(长链剖分+直径+找性质)的更多相关文章

  1. CF Contest 526 G. Spiders Evil Plan 长链剖分维护贪心

    LINK:Spiders Evil Plan 非常巧妙的题目. 选出k条边使得这k条边的路径覆盖x且覆盖的边的边权和最大. 类似于桥那道题还是选择2k个点 覆盖x那么以x为根做长链剖分即可. 不过这样 ...

  2. Codeforces 526G Spiders Evil Plan

    由于做的时候看的是中文题面,第一遍写就被卡题意了:还以为每一条都要过x,那么就是一道动态树根选择2y个叶子的奇怪题目 交完0分gg,才发现题目看错了╮(╯▽╰)╭ the node containin ...

  3. 2019.01.08 codeforces 1009F. Dominant Indices(长链剖分)

    传送门 长链剖分模板题. 题意:给出一棵树,设fi,jf_{i,j}fi,j​表示iii的子树中距离点iii距离为jjj的点的个数,现在对于每个点iii要求出使得fif_ifi​取得最大值的那个jjj ...

  4. Codeforces 1009 F. Dominant Indices(长链剖分/树上启发式合并)

    F. Dominant Indices 题意: 给一颗无向树,根为1.对于每个节点,求其子树中,哪个距离下的节点数量最多.数量相同时,取较小的那个距离. 题目: 这类题一般的做法是树上的启发式合并,复 ...

  5. Codeforces 1413F - Roads and Ramen(树的直径+找性质)

    Codeforces 题目传送门 & 洛谷题目传送门 其实是一道还算一般的题罢--大概是最近刷长链剖分,被某道长链剖分与直径结合的题爆踩之后就点开了这题. 本题的难点就在于看出一个性质:最长路 ...

  6. 2019.01.06 vijos lxhgww的奇思妙想(长链剖分)

    传送门 长链剖分模板题. 题意简述:允许O(nlogn)O(nlog_n)O(nlogn​)预处理,让你支持O(1)O(1)O(1)查找任意一个点的kkk级祖先. 思路:因为要O(1)O(1)O(1) ...

  7. 【CF526G】Spiders Evil Plan(贪心)

    [CF526G]Spiders Evil Plan(贪心) 题面 洛谷 CodeForces 给定一棵树,要求选择\(y\)条链,满足被链覆盖的所有点在树上联通,且\(x\)必定在联通块中. 对于每次 ...

  8. CF 1009 F Dominant Indices —— 长链剖分+指针

    题目:http://codeforces.com/contest/1009/problem/F 也可以用 dsu on tree 的做法,全局记录一个 dep,然后放进堆里,因为字典序要最小,所以再记 ...

  9. CF1009F Dominant Indices 长链剖分

    题目传送门 https://codeforces.com/contest/1009/problem/F 题解 长链剖分的板子吧. 令 \(dp[x][i]\) 表示 \(x\) 的子树中的深度为 \( ...

随机推荐

  1. 【原创】浅谈指针(五)const和指针

    前言 过了几个月再次更新.最近时间也不多了,快要期中考试了,暂且先少写一点吧. 本文仅在博客园发布,如在其他平台发现均为盗取,请自觉支持正版. 练习题 我们先来看几道题目.如果这几道题都不会的话,就先 ...

  2. 【UE4 C++】 射线检测 LineTrace 及 BoxTrace、SphereTrace、CapsuleTrace API

    World.h 库里的 Trace API Trace模式 TraceSingle 单个结果 TraceMulti 多个结果 Trace 的检测依据 ByChanne ByObjectType ByP ...

  3. 【BZOJ2070】列队春游———[组合数学+概率DP]

    数学渣滓不可做の题OTZ Description (单身人士不可做 Input                     |            Output 3                   ...

  4. 轻松掌握stm32直流电机驱动与测速

    说实话就现在的市场应用中stm32已经占到了绝对住到的地位,51已经成为过去式,32的功能更加强大,虽然相应的难度有所增加,但是依然阻止不了大家学习32的脚步,不说大话了这些大家都懂要不然也不会学习s ...

  5. Linux入门所必备的Linux命令和C语言基础

    文件和目录(底部有视频资料) cd /home 进入 '/ home' 目录' cd - 返回上一级目录 cd -/- 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 c ...

  6. hdu 3038 How Many Answers Are Wrong(并查集)

    题意: N和M.有N个数. M个回答:ai, bi, si.代表:sum(ai...bi)=si.如果这个回答和之前的冲突,则这个回答是假的. 问:M个回答中有几个是错误的. 思路: 如果知道sum( ...

  7. Swift-技巧(二)模糊脸部功能

    摘要 本文介绍模糊脸部的功能逻辑和实现方式,实现方式会尽可能的使用苹果提供的 API,保证功能高效率和简洁. 逻辑 模糊脸部的逻辑主要有两个流程,就是先找到脸部,然后模糊脸部,那么就引申出这两个实现问 ...

  8. oracle 归档日志:db_recovery_file_dest、log_archive_dest和log_archive_dest_n的区别和使用

    概念: db_recovery_file_dest:默认的指定闪回恢复区路径 log_archive_dest:指定归档文件存放的路径,所有归档路径必须是本地的,默认为''.log_archive_d ...

  9. C++11 多线程同步 互斥锁 条件变量

    在多线程程序中,线程同步(多个线程访问一个资源保证顺序)是一个非常重要的问题,Linux下常见的线程同步的方法有下面几种: 互斥锁 条件变量 信号量 这篇博客只介绍互斥量和条件变量的使用. 互斥锁和条 ...

  10. MySQL 默认隔离级别是RR,为什么阿里这种大厂会改成RC?

    我之前写过一篇文章<为什么MySQL选择REPEATABLE READ作为默认隔离级别?>介绍过MySQL 的默认隔离级别是 Repeatable Reads以及背后的原因. 主要是因为M ...