bzoj2599

2599: [IOI2011]Race

Time Limit: 70 Sec  Memory Limit: 128 MB
Submit: 2476  Solved: 733
[Submit][Status][Discuss]

Description

给一棵树,每条边有权.求一条路径,权值和等于K,且边的数量最小.

Input

第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

Output

一个整数 表示最小边数量 如果不存在这样的路径 输出-1

Sample Input

4 3
0 1 1
1 2 2
1 3 4

Sample Output

2

HINT

Source

题解:

  随便点分治,用一个ans[i]代表u,v距离为k,边数为i的点对个数,那么答案就为i (ans[i]>0 && i 最小);

  怎么求ans? 自己yy一下吧

代码:

  

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define N 200100
using namespace std;
int tot,n,m,all,k,root,sum;
int pre[N*2],v[N*2],val[N*2],now[N],size[N],ans[N],f[N];
bool vis[N];
struct data{int val,size;
}d[N],da[N];
bool cmp(data a,data b)
{
    return a.val<b.val;
}
int read()
{
    int x=0; char ch; bool bo=1;
    while (ch=getchar(),ch<‘0‘||ch>‘9‘) if (ch==‘-‘) bo=0;
    while (x=x*10+ch-‘0‘,ch=getchar(),ch>=‘0‘&&ch<=‘9‘);
    if (!bo) return -x; return x;
}
void ins(int a,int b,int c)
{
    ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c;
}
void getroot(int u,int fa)
{
    size[u]=1; f[u]=0;
    for (int p=now[u]; p; p=pre[p])
    {
        int son=v[p];
        if (vis[son]||son==fa) continue;
        getroot(son,u);
        size[u]+=size[son];
        f[u]=max(f[u],size[son]);
    }
    f[u]=max(f[u],all-size[u]);
    if (f[u]<f[root]) root=u;
}
void getarray(int u,int fa)
{
    for (int p=now[u]; p; p=pre[p])
    {
        int son=v[p];
        if (son==fa||vis[son]) continue;
        da[++sum].val=d[son].val=d[u].val+val[p];
        da[sum].size=d[son].size=d[u].size+1;
        getarray(son,u);
    }

}
void calc(int u,int value, int f)
{
    d[u].val=value; if (f==1) d[u].size=0; else d[u].size=1;
    sum=0; da[++sum].val=value; da[sum].size=d[u].size;
    getarray(u,0);
    sort(da+1,da+1+sum,cmp);
    for (int i=1,j=sum; i<=j; i++)
    {
        while (j>i && da[j].val+da[i].val>k) j--;
        for (int p=j; da[i].val+da[p].val==k ;p--)ans[da[p].size+da[i].size]+=f;
    }
}
void solve(int u)
{
    vis[u]=1; calc(u,0,1);
    for    (int p=now[u]; p; p=pre[p])
    {
        int son=v[p];
        if (vis[son]) continue;
        calc(son,val[p],-1);
        all=size[son];
        root=0; getroot(son,0);
        solve(root);
    }
}
int main()
{
    n=read(); k=read();
    for (int i=1; i<n; i++)
    {
        int u=read()+1,v=read()+1,value=read();
        ins(u,v,value); ins(v,u,value);
    }
    all=n; f[root=0]=n; getroot(1,0);
    solve(root);
    for (int i=1; i<n; i++)
    {
        if (ans[i])
        {
            printf("%d\n",i);
            return 0;
        }
    }
    printf("-1\n"); return 0;
} 

时间: 2024-12-14 03:41:59

bzoj2599的相关文章

bzoj2599 [ IOI2011] -- 点分治

令ans[i]表示权值和等于k的路径条数,然后点分治就可以了. 具体看代码. 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 #define N 200010 8 #define INF 2147483647 9 inline int M

bzoj1758 [Wc2010]重建计划 &amp; bzoj2599 [IOI2011]Race

两题都是树分治. 1758这题可以二分答案avgvalue,因为avgvalue=Σv(e)/s,因此二分后只需要判断Σv(e)-s*avgvalue是否大于等于0,若大于等于0则调整二分下界,否则调整二分上界.假设一棵树树根为x,要求就是经过树根x的最大答案,不经过树根x的可以递归求解.假设B[i]为当前做到的一颗x的子树中的点到x的距离为i的最大权值,A[i]为之前已经做过的所有子数中的点到x的距离为i的最大权值(这里的权值是Σv(e)-i*avgvalue),那么对于当前子树的一个距离i,

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

[BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 Input 第一行 两个整数 n, k第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始) Output 一个整数 表示最小边数量 如果不存在这样的路径 输出-1 Sample Input 4 3 0 1 1 1 2 2 1 3 4 Sample Output 2 题解:本题大

【BZOJ-2599】Race 点分治

2599: [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBSubmit: 2590  Solved: 769[Submit][Status][Discuss] Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 Input 第一行 两个整数 n, k第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始) Output

【点分治】【哈希表】bzoj2599 [IOI2011]Race

给nlog2n随便过的跪了,不得已弄了个哈希表伪装成nlogn(当然随便卡,好孩子不要学)…… 不过为啥哈希表的大小开小点就RE啊……?必须得超过数据范围一大截才行……谜 #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int f,c; inline void R(int &x){ c=0;f=1; for(;c<'0'||c>'9';c=getc

bzoj2599: [IOI2011]Race(点分治)

写了四五道点分治的题目了,算是比较理解点分治是什么东西了吧= = 点分治主要用来解决点对之间的问题的,比如距离为不大于K的点有多少对. 这道题要求距离等于K的点对中连接两点的最小边数. 那么其实道理是一样的.先找重心,然后先从重心开始求距离dis和边数num,更新ans,再从重心的儿子开始求得dis和num,减去这部分答案 因为这部分的答案中,从重心开始的两条链有重叠部分,所以要剪掉 基本算是模板题,但是减去儿子的答案的那部分还有双指针那里调了好久,所以还不算特别熟练.. PS跑了27秒慢到飞起

[BZOJ2599][Race][IOI2011]点分治

这是为了真正去学一下点分治..然后看了迪克李的ppt 又是一道写(改)了很久的题..终于ac了 1354799 orzliyicheng 2599 Accepted 31936 kb 23584 ms C++/Edit 2218 B 2016-03-27 15:55:17 不算快呢..具体实现是看的hzwer的blog,然而迪克李的ppt已经将想法讲得很清楚了 uoj文件里有,就懒得贴题解了 刚刚写完的时候,一个极限数据要跑60sec,我也是醉了..主要原因有: 1.清空数组的时候竟然跑了n遍f

[BZOJ2599][IOI2011]Race

试题描述 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 输入 第一行 两个整数 n, k第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始) 输出 一个整数 表示最小边数量 如果不存在这样的路径 输出-1 输入示例 4 3 0 1 1 1 2 2 1 3 4 输出示例 2 数据规模及约定 见“试题描述” 题解 点分治裸题.我还调了半天TAT...好久没写什么都忘了... 每次找子树的中心往下递

【BZOJ2599】[IOI2011]Race【点分治】

[题目链接] 点分治. 考虑经过点x的路径,对于x,用类似TreeDP的方法,记录no[d],表示路径长度为d时经过边最少的点的编号. 对于已经走过的子树,更新no.对于当前子树,遍历到一个点v,用depth[no[k - dis[v]]] + depth[v]更新答案. 注意给no清零时,用dfs姿势清零,这样做是O(n)的.如果直接用for或者memset,这样做是O(k)的,会TLE. /* Telekinetic Forest Guard */ #include <cstdio> #i