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. netty系列之:让TLS支持http2

    目录 简介 TLS的扩展协议NPN和ALPN SslProvider ApplicationProtocolConfig 构建SslContext ProtocolNegotiationHandler ...

  2. python常用内置函数(转载)

    1. 和数字相关 1.1 数据类型 1.2 进制转换 1.3 数学运算 2. 和数据结构相关 2.1 序列 2.2 数据集合 2.3 相关内置函数 3. 和作用域相关 4. 和迭代器生成器相关 5. ...

  3. MySQL 8.0安装 + 配置环境变量 + 连接 cmd

    MySQL 安装教程 搜索 MySQL,进入官网,找到 download 点击适用于 window community 版本,点击图中第二个 450.7 M 的安装包进行下载 这里有五个选项,选择第二 ...

  4. eureka服务端和客户端的简单搭建

    本篇博客简单记录一下,eureka 服务端和 客户端的简单搭建. 目标: 1.完成单机 eureka server 和 eureka client 的搭建. 2.完成eureka server 的添加 ...

  5. 2021.8.21考试总结[NOIP模拟45]

    T1 打表 由归纳法可以发现其实就是所有情况的总和. $\frac{\sum_{j=1}^{1<<k}(v_j-v_{ans})}{2^k}$ $code:$ 1 #include< ...

  6. 2021.8.3考试总结[NOIP模拟29]

    T1 最长不下降子序列 数据范围$1e18$很不妙,但模数$d$只有$150$,考虑从这里突破. 计算的式子是个二次函数,结果只与上一个值有关,而模$d$情况下值最多只有$150$个,就证明序列会出现 ...

  7. 进程间通信消息队列msgsnd执行:Invlid argument——万恶的经验主义

    最近在搞进程间通信,首先在我的ubuntu 14.04上写了接口和测试demo,编译和执行都OK,,代码如下: 接口文件ipcmsg.h /* ipcmsg.h */ #ifndef H_MSGIPC ...

  8. 数组中出现次数超过一半的数字 牛客网 剑指Offer

    数组中出现次数超过一半的数字 牛客网 剑指Offer 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字 ...

  9. 让 AI 为你写代码 - 体验 Github Copilot

    前几天在群里看到有大神分享 Copoilot AI 写代码,看了几个截图有点不敢相信自己的眼睛.今天赶紧自己也来体验一下 Copoilot AI 写代码到底有多神奇. 申请 现在 Copoilot 还 ...

  10. 关于Arrays类的静态方法asList()

    Array.asList():是数组转成集合的方法 List<String> list = Arrays.asList(new String[]{"AA", " ...