浅谈树分治:https://www.cnblogs.com/AKMer/p/10014803.html

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2599

我们设\(f_i\)为长度为\(i\)的路径边数最小可以是多少,依次遍历当前根的子树,先用\(cnt+f[k-dis]\)更新答案,再遍历第二遍当前子树更新\(f\)数组。\(cnt\)表示根到当前点一共经过了多少条边。因为\(k\)的范围是\(10^6\)级别的,每次处理当前联通块前把\(f\)数组全部赋成极大值会很慢,所以我们每次更新\(f\)数组的时候把被改动过的\(dis\)用栈记录下来,每次处理完当前联通块就弹栈并且把相应的\(f\)数组初始化,这样做就是\(O(n)\)级别的了。

如果用边分做的话,记得把新建的边权值赋成\(-1\),因为可能会有边权为\(0\)的边,然后统计\(cnt\)的时候只有在碰到权值不为\(-1\)的边才算。

时间复杂度:\(O(nlogn)\)

空间复杂度:\(O(n)\)

点分治版代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int maxn=2e5+5,maxm=1e6+5;; bool vis[maxn],insta[maxm];
int n,m,tot,mx,rt,N,ans,top;
int siz[maxn],f[maxm],sta[maxn];
int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2]; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} void add(int a,int b,int c) {
pre[++tot]=now[a];
now[a]=tot,son[tot]=b,val[tot]=c;
} void find_rt(int fa,int u) {
int res=0;siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)find_rt(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
res=max(res,N-siz[u]);
if(res<mx)mx=res,rt=u;
} void query(int fa,int u,int cnt,int dis) {
if(dis<=m)ans=min(ans,cnt+f[m-dis]);siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)query(u,v,cnt+1,dis+val[p]),siz[u]+=siz[v];
} void solve(int fa,int u,int cnt,int dis) {
if(dis>m)return;
f[dis]=min(f[dis],cnt);
if(!insta[dis])sta[++top]=dis,insta[dis]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)solve(u,v,cnt+1,dis+val[p]);
} void work(int u,int size) {
N=size,mx=rt=n+1,find_rt(0,u),u=rt,vis[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v])query(u,v,1,val[p]),solve(u,v,1,val[p]);
ans=min(ans,f[m]);
while(top) {
f[sta[top]]=f[m+1];
insta[sta[top--]]=0;
}
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v])work(v,siz[v]);
} int main() {
n=read(),m=read();
for(int i=1;i<n;i++) {
int a=read()+1,b=read()+1,c=read();
add(a,b,c),add(b,a,c);
}
memset(f,127/3,sizeof(f));
ans=f[0];work(1,n);
if(ans==f[m+1])puts("-1");
else printf("%d\n",ans);
return 0;
}

边分治版代码如下:

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int> pii;
#define fr first
#define sc second const int maxn=4e5+5,maxm=1e6+5; bool vis[maxn],insta[maxm];
int f[maxm],siz[maxn],sta[maxn];
int n,m,tot,cnt,mx,id,N,top,ans,tmp1,tmp2;
int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2]; vector<pii>to[maxn];
vector<pii>::iterator it; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} void add(int a,int b,int c) {
pre[++tot]=now[a];
now[a]=tot,son[tot]=b,val[tot]=c;
} void find_son(int fa,int u) {
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(v!=fa)to[u].push_back(make_pair(v,val[p])),find_son(u,v);
} void rebuild() {
tot=1;memset(now,0,sizeof(now));
for(int i=1;i<=cnt;i++) {
int size=to[i].size();
if(size<=2) {
for(it=to[i].begin();it!=to[i].end();it++) {
pii tmp=*it;
add(i,tmp.fr,tmp.sc),add(tmp.fr,i,tmp.sc);
}
}
else {
pii u1=make_pair(++cnt,-1),u2;
if(size==3)u2=to[i].front();
else u2=make_pair(++cnt,-1);
add(i,u1.fr,u1.sc),add(u1.fr,i,u1.sc);
add(i,u2.fr,u2.sc),add(u2.fr,i,u2.sc);
if(size==3) {
for(int j=1;j<=2;j++)
to[cnt].push_back(to[i].back()),to[i].pop_back();
}
else {
int p=0;
for(it=to[i].begin();it!=to[i].end();it++) {
if(!p)to[cnt-1].push_back(*it);
else to[cnt].push_back(*it);p^=1;
}
}
}
}
} void find_edge(int fa,int u) {
siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa) {
find_edge(u,v),siz[u]+=siz[v];
if(abs(N-2*siz[v])<mx)mx=abs(N-2*siz[v]),id=p>>1;
}
} void solve(int fa,int u,int num,int dis) {
if(dis<=m) {
f[dis]=min(f[dis],num);
if(!insta[dis])sta[++top]=dis,insta[dis]=1;
}siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)
solve(u,v,num+(val[p]!=-1),dis+(val[p]!=-1)*val[p]),siz[u]+=siz[v];
} void query(int fa,int u,int num,int dis) {
if(dis+tmp1<=m)ans=min(ans,num+f[m-dis-tmp1]+tmp2);siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)
query(u,v,num+(val[p]!=-1),dis+(val[p]!=-1)*val[p]),siz[u]+=siz[v];
} void work(int u,int size) {
N=size,mx=id=cnt+1,find_edge(0,u);
if(id==cnt+1)return;vis[id]=1;
int u1=son[id<<1],u2=son[id<<1|1];
tmp1=(val[id<<1]!=-1)*val[id<<1],tmp2=(val[id<<1]!=-1);
solve(0,u1,0,0),query(0,u2,0,0);
while(top) {
f[sta[top]]=f[m+1];
insta[sta[top--]]=0;
}
work(u1,siz[u1]),work(u2,siz[u2]);
} int main() {
cnt=n=read(),m=read();
for(int i=1;i<n;i++) {
int a=read()+1,b=read()+1,c=read();
add(a,b,c),add(b,a,c);
}
find_son(0,1),rebuild();
memset(f,127/3,sizeof(f));
ans=f[0];work(1,cnt);
if(ans==f[m+1])puts("-1");
else printf("%d\n",ans);
return 0;
}

BZOJ2599:[IOI2011]Race的更多相关文章

  1. 【BZOJ2599】[IOI2011]Race 树的点分治

    [BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 100000 ...

  2. bzoj2599/luogu4149 [IOI2011]Race (点分治)

    点分治.WA了一万年. 重点就是统计答案的方法 做法一(洛谷AC bzojWA 自测WA): 做点x时记到x距离为k的边数最小值为dis[k],然后对每一对有值的dis[i]和dis[K-i],给an ...

  3. Luogu4149:[IOI2011]Race

    题目 bzoj权限题... Luogu Sol 点分治辣,边权非负,k>=1,开个\(1e6\)的桶就好辣 # include <bits/stdc++.h> # define RG ...

  4. [bzoj2599][IOI2011]Race——点分治

    Brief Description 给定一棵带权树,你需要找到一个点对,他们之间的距离为k,且路径中间的边的个数最少. Algorithm Analyse 我们考虑点分治. 对于子树,我们递归处理,所 ...

  5. [IOI2011]Race

    2599: [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBhttp://www.lydsy.com/JudgeOnline/problem ...

  6. 模板—点分治B(合并子树)(洛谷P4149 [IOI2011]Race)

    洛谷P4149 [IOI2011]Race 点分治作用(目前只知道这个): 求一棵树上满足条件的节点二元组(u,v)个数,比较典型的是求dis(u,v)(dis表示距离)满足条件的(u,v)个数. 算 ...

  7. BZOJ 2599: [IOI2011]Race( 点分治 )

    数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...

  8. [IOI2011]Race 点分治

    [IOI2011]Race LG传送门 点分治板子题. 直接点分治统计,统计的时候开个桶维护下就好了. 注(tiao)意(le)细(hen)节(jiu). #include<cstdio> ...

  9. bzoj 2599 [IOI2011]Race 点分

    [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBSubmit: 4768  Solved: 1393[Submit][Status][Dis ...

随机推荐

  1. C 标准库 - <errno.h>

    C 标准库 - <errno.h> 简介 C 标准库的 errno.h 头文件定义了整数变量 errno,它是通过系统调用设置的,在错误事件中的某些库函数表明了什么发生了错误.该宏扩展为类 ...

  2. 更改已经签名的app中的内容

    转载请说明出处http://blog.csdn.net/andywuchuanlong 记得上次在南昌中兴的一个项目中遇到过一个这种需求:一个app能够给多个渠道商去运营,渠道商推广出去能够获得对应的 ...

  3. 无法获取html元素宽高度的问题

    今天遇到了xxx.style.width无法获取元素宽度的问题,原来一直没有注意到这个小细节: 1)如果width:120px:是写在样式表里面的,获取宽度或者高度的方法是: xxx.offsetWi ...

  4. iOS GCD使用

    Grand Central Dispatch(GCD)是异步运行任务的技术之中的一个. 一般将应用程序中记述的线程管理用的代码在系统级中实现.开发人员仅仅须要定义想运行的任务并追加到适当的Dispat ...

  5. CSS盒模型之三角形

    W3上介绍盒模型: 这里教程,但是太过于简单了,http://www.w3.org/community/webed/wiki/CSS/Training/Box_model. 如图,盒模型和背景属性控制 ...

  6. 在命令行下运行Matlab

    2014-04-20 22:08:11 在命令行下执行: matlab -help 可以得到帮助文件: Usage: matlab [-h|-help] | [-n | -e] [-arch | v= ...

  7. GreenPlum 安装方法详解

    一.安装环境准备 1.磁盘环境准备 磁盘分区典型配置如下: 文件系统 文件格式    大小  /        ext3   50GB,Linux系统的根目录,所有的目录都挂在这个目录下面,建议大小为 ...

  8. 使用Entity Framework和WCF Ria Services开发SilverLight之6:查找指定字段

    对数据库表指定字段的查找,又是实际工作中的一项必要工作.SL客户端仅获取实际需要的指定的字段,好处很多,比如:有助于减少网络流量. 有两类这样的使用场景. 1:联表查询不需要外键表 在上一篇中,我们使 ...

  9. 程序猿的量化交易之路(27)--Cointrader之PriceData价格数据(14)

    转载须注明出处:http://blog.csdn.net/minimicall?viewmode=contents,http://cloudtrade.top/ PriceData:价格数据.价格数据 ...

  10. secureCRT与vim配置

    折腾了一天,给服务器新装了centos系统,用crt连接,vim用着很不习惯. 修改配色,快捷键啥的都不怎么起效. 后来发现.crt里的会话选项-终端-仿真 里配成xtream,使用颜色方案就可以了.