BZOJ2599:[IOI2011]Race
浅谈树分治: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的更多相关文章
- 【BZOJ2599】[IOI2011]Race 树的点分治
[BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 100000 ...
- bzoj2599/luogu4149 [IOI2011]Race (点分治)
点分治.WA了一万年. 重点就是统计答案的方法 做法一(洛谷AC bzojWA 自测WA): 做点x时记到x距离为k的边数最小值为dis[k],然后对每一对有值的dis[i]和dis[K-i],给an ...
- Luogu4149:[IOI2011]Race
题目 bzoj权限题... Luogu Sol 点分治辣,边权非负,k>=1,开个\(1e6\)的桶就好辣 # include <bits/stdc++.h> # define RG ...
- [bzoj2599][IOI2011]Race——点分治
Brief Description 给定一棵带权树,你需要找到一个点对,他们之间的距离为k,且路径中间的边的个数最少. Algorithm Analyse 我们考虑点分治. 对于子树,我们递归处理,所 ...
- [IOI2011]Race
2599: [IOI2011]Race Time Limit: 70 Sec Memory Limit: 128 MBhttp://www.lydsy.com/JudgeOnline/problem ...
- 模板—点分治B(合并子树)(洛谷P4149 [IOI2011]Race)
洛谷P4149 [IOI2011]Race 点分治作用(目前只知道这个): 求一棵树上满足条件的节点二元组(u,v)个数,比较典型的是求dis(u,v)(dis表示距离)满足条件的(u,v)个数. 算 ...
- BZOJ 2599: [IOI2011]Race( 点分治 )
数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...
- [IOI2011]Race 点分治
[IOI2011]Race LG传送门 点分治板子题. 直接点分治统计,统计的时候开个桶维护下就好了. 注(tiao)意(le)细(hen)节(jiu). #include<cstdio> ...
- bzoj 2599 [IOI2011]Race 点分
[IOI2011]Race Time Limit: 70 Sec Memory Limit: 128 MBSubmit: 4768 Solved: 1393[Submit][Status][Dis ...
随机推荐
- C#应用程序配置文件.config介绍
我们经常会希望在程序中写入一些配置信息,例如版本号,以及数据库的连接字符串等.你可能知道在WinForm应用程序中可以利用Properties.Settings来进行类似的工作,但这些其实都利用了Ap ...
- vue class绑定 组件
当在一个自定义组件上使用 class 属性时,这些类将被添加到该组件的根元素上面.这个元素上已经存在的类不会被覆盖. 例如,如果你声明了这个组件: Vue.component('my-componen ...
- C结构体之位域(位段)(转)
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位.例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可.为了节省存储空间,并使处理简便,C语言又提供了一种数据结构 ...
- Atitit.ati  str  字符串增强api
Atitit.ati str 字符串增强api 1. java StringUtils方法全览 分类: Java2011-11-30 17:22 8194人阅读 评论(2) 收藏 举报 javas ...
- python(37)- 软件开发规范
软件开发规范 一.为什么要设计好目录结构? 1.可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等.从而非常快速的了解这个项目. 2 ...
- MySQL 建立外键约束
http://www.jzxue.com/shujuku/mysql/201109/06-8742.html MySQL 建立外键约束的语法太晦涩难懂了, 不得不记下笔记. 1. 在建表时建立外键 C ...
- Allegro布线基本操作
转:allegro基本步骤 常见问题 cadence16.5中电源线.地线取消飞线显示 目录: 一.Allegro基本技巧 1.关闭电源和地网络的飞线 2.开启特定NET飞线 3.元器件快速对齐(待完 ...
- WPF03(样式)
说起样式,大家第一反应肯定是css,好的,先上一段代码. 1 html{border:0;} 2 ul,form{margin:0; padding:0} 3 body,div,th,td,li,dd ...
- adb tcp 调试
su setprop service.adb.tcp.port 5555 stop adbd start adbd
- 记录一次在 VirtualBox的添加共享windows文件后,发现没有共享文件的事
在VirtualBox设置完桥接添加ip后,在设备中添加共享windows文件,“e:\work ”,发现共享目录没有文件.使用了各种reboot之后,还是没有发现共享文件夹,重新设置还是不行,用mo ...