卧薪尝胆,厚积薄发。
IOI2011 Race
Date: Sat Aug 04 11:59:59 CST 2018 In Category: NoCategory

Description:

给一棵树,每条边有权。求一条简单路径,权值和等于 $K$ ,且边的数量最小。
$1\le n \le 200000$

Solution:

点分治, $calc$ 时记录一下长为 $l$ 时边的最小数量,每次枚举这个点的所有子树,先更新答案,再统一把这个子树中的值加到 $map$ 里供之后的子树查询。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<map>
#include<cstring>
using namespace std;
inline int rd()
{
register int res = 0,f = 1;
register char c = getchar();
while(!isdigit(c))
{
if(c == '-')f = -1;
c = getchar();
}
while(isdigit(c))
{
res = (res << 1) + (res << 3) + c - '0';
c = getchar();
}
return res * f;
}
int n,l;
#define MAXN 200010
int ans = 0x3f3f3f3f;
struct edge
{
int to,nxt,v;
}e[MAXN << 1];
int edgenum = 0;
int lin[MAXN] = {0};
inline void add(int a,int b,int c)
{
++edgenum;e[edgenum].to = b;e[edgenum].v = c;e[edgenum].nxt = lin[a];lin[a] = edgenum;
++edgenum;e[edgenum].to = a;e[edgenum].v = c;e[edgenum].nxt = lin[b];lin[b] = edgenum;
return;
}
#define INF 0x3f3f3f3f
int root,s;
int siz[MAXN],son[MAXN],d[MAXN];
bool v[MAXN];
void getroot(int k,int fa)
{
siz[k] = 1;d[k] = 0;
for(register int i = lin[k];i != 0;i = e[i].nxt)
{
if(v[e[i].to] || e[i].to == fa)continue;
getroot(e[i].to,k);
siz[k] += siz[e[i].to];
d[k] = max(d[k],siz[e[i].to]);
}
d[k] = max(d[k],s - d[k]);
if(d[k] < d[root])root = k;
return;
}
#define fi first
#define se second
pair<int,int> deep[MAXN];
int tot = 0;
void getdeep(int k,int fa,int dep,int cnt)
{
deep[++tot] = make_pair(dep,cnt);
for(register int i = lin[k];i != 0;i = e[i].nxt)
if(!v[e[i].to] && e[i].to != fa)
getdeep(e[i].to,k,dep + e[i].v,cnt + 1);
return;
}
int p[1000010];
int stack[MAXN],top = 0;
#define INF 0x3f3f3f3f
void calc(int k)
{
p[0] = 0;
top = 0;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(v[e[i].to])continue;
tot = 0;
getdeep(e[i].to,0,e[i].v,1);
for(int j = 1;j <= tot;++j)
{
if(deep[j].fi <= l)ans = min(ans,p[l - deep[j].fi] + deep[j].se);
}
for(int j = 1;j <= tot;++j)
{
if(deep[j].fi <= l)
{
p[deep[j].fi] = min(p[deep[j].fi],deep[j].se);
stack[++top] = deep[j].fi;
}
}
}
for(int i = 1;i <= top;++i)p[stack[i]] = INF;
return;
}
void divide(int k)
{
v[k] = true;
calc(k);
for(register int i = lin[k];i != 0;i = e[i].nxt)
{
if(!v[e[i].to])
{
root = 0;s = siz[e[i].to];
getroot(e[i].to,0);
divide(root);
}
}
return;
}
int main()
{
memset(p,0x3f,sizeof(p));
p[0] = 0;
scanf("%d%d",&n,&l);
register int a,b,c;
for(register int i = 1;i < n;++i)
{
a = rd();b = rd();c = rd();
++a;++b;
add(a,b,c);
}
root = 0;s = n;d[0] = INF;
getroot(1,0);
divide(root);
if(ans != 0x3f3f3f3f)printf("%d\n",ans);
else puts("-1");
return 0;
}
In tag: 树-点分治
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡