bzoj-2051 A Problem For Fun

题意:

给出一棵n个结点的树,边上有权值;

对于每个点求离它第k小的距离;

n<=50000;

题解:

正解似乎是树分治维护距离,然后二分答案啥的,时间复杂度O(nlog^3);

但是如果想不到树分治怎么办呢?那么就来写一个逗比做法吧!

考虑从一个点转移到另外一个点,这个转移过程对于一些点是增加这条边的权值,另一些是减少这条边的权值;

而投影到DFS序上,就是对于子树区间的加减修改;

从而将原题转化成区间修改+全局K小值的问题。。。

呵呵

于是我去膜了膜wyfcyx大爷,得到了一个污算法。。

反正不可做,那就分块吧(设每块的大小为B);

每个块内维护一个有序序列,查询时二分答案,然后统计小于当前mid的有多少数;

那么初始化就是n/B*BlogB=nlogB,每次查询都是log(ans)*n/B*logB;

每次修改整块的修改打标记,非整块的暴力修改,然后利用归并排序将序列重新排成有序;

这样每一次的修改就是n/B+B;

所以我们令B=√n*logn,时间复杂度为O(n*(log(ans)*√n+√n/logn+√n*logn)),大概就是O(n√nlogn)的啦。。。;

讲道理,我们这个√n比log^2n还是小一点的呢。。

然而实际上隐藏了一些常数。。不过对于极限数据,5s以内还是可以出解的;

bzoj 4317: Atm的树 双倍经验;

代码:

#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 51000
#define M 500000000
#define MEM 1000000
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
using namespace std;
int next[N],to[N],val[N],head[N],ce;
int L[N],R[N],tot;
int n,k,B,cnt,ans[N];
int dis[N],cov[N];
struct node
{
    int* t;
    int no;
    friend bool operator <(node a,node b)
    {
        return *a.t+cov[a.no]<*b.t+cov[b.no];
    }
}p[N];
void add(int x,int y,int v)
{
    to[++ce]=y;
    next[ce]=head[x];
    val[ce]=v;
    head[x]=ce;
}
void dfs(int x,int d)
{
    L[x]=++tot;
    dis[L[x]]=d;
    for(int i=head[x];i;i=next[i])
        dfs(to[i],d+val[i]);
    R[x]=tot;
}

void Build(int x)
{
    int i,l=max(x*B,1),r=min((x+1)*B,n+1);
    for(i=l;i<r;i++)
        p[i].t=&dis[i],p[i].no=x;
    sort(p+l,p+r);
}
int calc(node t)
{
    int i,ret=0,l,r;
    for(i=0;i<=cnt;i++)
    {
        l=max(i*B,1),r=min((i+1)*B,n+1);
        ret+=lower_bound(p+l,p+r,t)-p-l;
    }
    return ret;
}
int query()
{
    int l,r,mid;
    node t;
    t.t=&mid,t.no=cnt+1;
    l=0,r=M;
    while(l<=r)
    {
        mid=l+r>>1;
        if(calc(t)+1<=k)
            l=mid+1;
        else
            r=mid-1;
    }
    return r;
}
void update(int l,int r,int v)
{
    static node st[N][2];
    int i,j,k,tl,tr,top[2];
    for(i=0;i<l/B;i++)
        cov[i]+=v;
    for(i++;i<r/B;i++)
        cov[i]-=v;
    for(i=r/B+1;i<=cnt;i++)
        cov[i]+=v;
    if(l/B!=r/B)
    {
        tl=max(l/B*B,1),tr=min((l/B+1)*B,n+1);
        for(i=tl;i<tr;i++)
        {
            if(i<l)
                dis[i]+=v;
            else
                dis[i]-=v;
        }
        top[0]=top[1]=0;
        for(i=tl;i<tr;i++)
        {
            if(p[i].t>=&dis[l])
                st[++top[0]][0]=p[i];
            else
                st[++top[1]][1]=p[i];
        }
        for(i=tl,j=1,k=1;i<tr;i++)
        {
            if(j<=top[0]&&k<=top[1])
                p[i]=*st[j][0].t<*st[k][1].t?st[j++][0]:st[k++][1];
            else
                p[i]=j<=top[0]?st[j++][0]:st[k++][1];
        }
        tl=max(r/B*B,1),tr=min((r/B+1)*B,n+1);
        for(i=tl;i<tr;i++)
        {
            if(i<=r)
                dis[i]-=v;
            else
                dis[i]+=v;
        }
        top[0]=top[1]=0;
        for(i=tl;i<tr;i++)
        {
            if(p[i].t<=&dis[r])
                st[++top[0]][0]=p[i];
            else
                st[++top[1]][1]=p[i];
        }
        for(i=tl,j=1,k=1;i<tr;i++)
        {
            if(j<=top[0]&&k<=top[1])
                p[i]=*st[j][0].t<*st[k][1].t?st[j++][0]:st[k++][1];
            else
                p[i]=j<=top[0]?st[j++][0]:st[k++][1];
        }
    }
    else
    {
        tl=max(l/B*B,1),tr=min((l/B+1)*B,n+1);
        for(i=tl;i<tr;i++)
        {
            if(i<l||i>r)
                dis[i]+=v;
            else
                dis[i]-=v;
        }
        top[0]=top[1]=0;
        for(i=tl;i<tr;i++)
        {
            if(p[i].t<&dis[l]||p[i].t>&dis[r])
                st[++top[0]][0]=p[i];
            else
                st[++top[1]][1]=p[i];
        }
        for(i=tl,j=1,k=1;i<tr;i++)
        {
            if(j<=top[0]&&k<=top[1])
                p[i]=*st[j][0].t<*st[k][1].t?st[j++][0]:st[k++][1];
            else
                p[i]=j<=top[0]?st[j++][0]:st[k++][1];
        }

    }
}
void slove(int x)
{
    ans[x]=query();
    for(int i=head[x];i;i=next[i])
    {
        update(L[to[i]],R[to[i]],val[i]);
        slove(to[i]);
        update(L[to[i]],R[to[i]],-val[i]);
    }
}
int main()
{
    int i,x,y,v;
    scanf("%d%d",&n,&k);
    k++;
    B=(int)sqrt(n)*log2(n);
    cnt=n/B;
    for(i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&v);
        add(x,y,v);
    }
    dfs(1,0);
    for(i=0;i<=cnt;i++)
        Build(i);
    slove(1);
    for(i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}
时间: 2024-08-19 14:10:55

bzoj-2051 A Problem For Fun的相关文章

[BZOJ 4999]This Problem Is Too Simple!

[BZOJ 4999]This Problem Is Too Simple! 题目 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. INPUT 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初始值. 接下来N

BZOJ 2301: [HAOI2011]Problem b(莫比乌斯反演 + 容斥原理 + 分块优化)

传送门 Problem 2301. – [HAOI2011]Problem b 2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 3671  Solved: 1643[Submit][Status][Discuss] Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input

BZOJ 2302: [HAOI2011]Problem c( dp )

dp(i, j)表示从i~N中为j个人选定的方案数, 状态转移就考虑选多少人为i编号, 然后从i+1的方案数算过来就可以了. 时间复杂度O(TN^2) --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long lo

[BZOJ 2301] [HAOI2011] Problem b

2301: [HAOI2011]Problem b Time Limit: 50 SecMemory Limit: 256 MB Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input 第一行一个整数n,接下来n行每行五个整数,分别表示a.b.c.d.k Output 共n行,每行一个整数表示满足要求的数对(x,y)的个数 Sample Input 2 2 5 1 5

bzoj 2301: [HAOI2011]Problem b mobius反演 RE

http://www.lydsy.com/JudgeOnline/problem.php?id=2301 设f(i)为在区间[1, n]和区间[1, m]中,gcd(x, y) = i的个数. 设F(i)为在区间[1, n]和区间[1, m]中,gcd(x, y) % i == 0的个数,很简单的公式就是floor(n / i) * floor(m / i) 可知gcd(x, y) = k * i也属于F(i)的范围,所以可以反演得到f(i)的表达式. 算一次复杂度O(n),而且询问区间的时候要

BZOJ 2301([HAOI2011]Problem b-mobius反演)

2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MB Submit: 2170  Solved: 934 [Submit][Status][Discuss] Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input 第一行一个整数n,接下来n行每行五个整数,分别表示a.b.c.d.k Out

BZOJ 2301 [HAOI2011]Problem b (容斥+莫比乌斯反演+分块优化 详解)

2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MB Submit: 2096  Solved: 909 [Submit][Status][Discuss] Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input 第一行一个整数n,接下来n行每行五个整数,分别表示a.b.c.d.k Out

BZOJ 2302: [HAOI2011]Problem c [DP 组合计数]

2302: [HAOI2011]Problem c Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 648  Solved: 355[Submit][Status][Discuss] Description 给n个人安排座位,先给每个人一个1~n的编号,设第i个人的编号为ai(不同人的编号可以相同),接着从第一个人开始,大家依次入座,第i个人来了以后尝试坐到ai,如果ai被占据了,就尝试ai+1,ai+1也被占据了的话就尝试ai+2,……,如果一直

bzoj 3339: Rmq Problem

3339: Rmq Problem Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1270  Solved: 666[Submit][Status][Discuss] Description Input Output Sample Input 7 5 0 2 1 0 1 3 2 1 3 2 3 1 4 3 6 2 7 Sample Output 3 0 3 2 4 HINT Source By Xhr 嗯,莫队 懒得敲线段树,毕竟线段树比较短 #

bzoj 2301: [HAOI2011]Problem b 莫比乌斯反演

2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 3679  Solved: 1648[Submit][Status][Discuss] Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input 第一行一个整数n,接下来n行每行五个整数,分别表示a.b.c.d.k Outp