bzoj-2051 A Problem For Fun
2015-11-26 12:56
330 查看
题意:
给出一棵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
,to
,val
,head
,ce;
int L
,R
,tot;
int n,k,B,cnt,ans
;
int dis
,cov
;
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
;
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
[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;
}
给出一棵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
,to
,val
,head
,ce;
int L
,R
,tot;
int n,k,B,cnt,ans
;
int dis
,cov
;
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
;
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
[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;
}
相关文章推荐
- C++实现八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序等
- Java实现八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序等
- Java排序算法总结之归并排序
- C++归并排序算法实例
- Javascript排序算法之合并排序(归并排序)的2个例子
- 归并排序的递归实现与非递归实现代码
- java二路归并排序示例分享
- java实现归并排序算法
- 归并排序的实现代码与思路
- leetcode 虐我篇之(二) Two Sum
- In-place Merge Sort 原地并归排序
- 使用Java完成《算法导论》习题2.3-2
- 插入排序移动次数
- 归并排序 with Python
- 归并排序-逆序对的求解
- 排序算法的复杂度和稳定性
- 用python实现归并排序
- (转)排序算法的稳定与不稳定
- BZOJ3275 Number (最小割)
- C语言--归并排序算法实现