bzoj2006 [ NOI2010 ] && bzoj3784 --点分治+线段树+堆

bzoj2006:

定义一个四元组{x,l,r,w},表示左端点在x,右端点在[l,r]的超级和弦的最大美妙度在将w作为右端点时取到,w可以用前缀和+线段树/ST表求出。

对于每个i,我们将{i,i+L-1,i+R-1,w}放入一个大根堆中,每次取出美妙度最大的一个加到答案中,并将{i,l,w-1,x},{i,w+1,r,x}放入堆中。

这样就相当于将左端点在i、右端点在w的超级和弦去掉。做k次就可以了。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #include<algorithm>
 6 using namespace std;
 7 inline char Nc(){
 8     static char buf[100000],*p1=buf,*p2=buf;
 9     if(p1==p2){
10         p2=(p1=buf)+fread(buf,1,100000,stdin);
11         if(p1==p2)return EOF;
12     }
13     return *p1++;
14 }
15 inline void Read(int& x){
16     char c=Nc(),b=1;
17     for(;c<‘0‘||c>‘9‘;c=Nc())if(c==‘-‘)b=-1;
18     for(x=0;c>=‘0‘&&c<=‘9‘;x=(x<<3)+(x<<1)+c-48,c=Nc());x*=b;
19 }
20 inline int _Max(int x,int y){return x<y?y:x;}
21 inline int _Min(int x,int y){return x<y?x:y;}
22 #define N 500010
23 #define INF 2147483647
24 int i,j,k,n,m,c[N<<2],s[N],x,L,R,t[N<<2];
25 struct G{
26     int x,l,r,w;
27     G(){}
28     G(int x,int l,int r,int w):x(x),l(l),r(r),w(w){}
29     bool operator<(G a)const{
30         return s[w]-s[x-1]<s[a.w]-s[a.x-1];
31     }
32 }tmp;
33 priority_queue<G>q;
34 long long Ans;
35 inline int Query(int Node,int l,int r,int L,int R){
36     if(l>R||r<L)return 0;
37     if(l>=L&&r<=R)return Node;
38     int Mid=l+r>>1;
39     int Q1=Query(Node<<1,l,Mid,L,R),Q2=Query(Node<<1|1,Mid+1,r,L,R);
40     return c[Q1]>c[Q2]?Q1:Q2;
41 }
42 inline void Build(int Node,int l,int r){
43     if(l==r){
44         c[Node]=s[l];
45         t[Node]=l;
46         return;
47     }
48     int Mid=l+r>>1;
49     Build(Node<<1,l,Mid);
50     Build(Node<<1|1,Mid+1,r);
51     if(c[Node<<1]>c[Node<<1|1])c[Node]=c[Node<<1],t[Node]=t[Node<<1];else
52     c[Node]=c[Node<<1|1],t[Node]=t[Node<<1|1];
53 }
54 int main()
55 {
56     Read(n);Read(k);Read(L);Read(R);
57     for(i=1;i<=n;i++)Read(x),s[i]=s[i-1]+x;
58     Build(1,1,n);c[0]=-INF;
59     for(i=1;i+L-1<=n;i++)q.push(G(i,i+L-1,_Min(i+R-1,n),t[Query(1,1,n,i+L-1,_Min(i+R-1,n))]));
60     while(k--){
61         tmp=q.top();q.pop();
62         Ans+=s[tmp.w]-s[tmp.x-1];
63         if(tmp.w>tmp.l)q.push(G(tmp.x,tmp.l,tmp.w-1,t[Query(1,1,n,tmp.l,tmp.w-1)]));
64         if(tmp.w<tmp.r)q.push(G(tmp.x,tmp.w+1,tmp.r,t[Query(1,1,n,tmp.w+1,tmp.r)]));
65     }
66     printf("%lld",Ans);
67     return 0;
68 }

bzoj2006

bzoj3784:

这题是bzoj2006的树上版本。考虑点分治。分治到一个点时,将所有点到它的距离d求出,那么一个子树中的点的d加上另一个子树中的点的d就是一条路径。

将dfs到的点依次加入一个序列,那么序列中每个点能到的所有点就是一个区间[l,r],就可以转化为bzoj2006了。

具体看代码。

代码:

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<queue>
  5 using namespace std;
  6 inline char Nc(){
  7     static char buf[100000],*p1=buf,*p2=buf;
  8     if(p1==p2){
  9         p2=(p1=buf)+fread(buf,1,100000,stdin);
 10         if(p1==p2)return EOF;
 11     }
 12     return *p1++;
 13 }
 14 inline void Read(int& x){
 15     char c=Nc(),b=1;
 16     for(;c<‘0‘||c>‘9‘;c=Nc())if(c==‘-‘)b=-1;
 17     for(x=0;c>=‘0‘&&c<=‘9‘;x=(x<<3)+(x<<1)+c-48,c=Nc());x*=b;
 18 }
 19 inline int _Max(int x,int y){return x>y?x:y;}
 20 #define N 50010
 21 #define INF 2147483647
 22 struct Edge{
 23     int t,nx,w;
 24 }e[N<<1];
 25 int i,j,k,n,Cnt,m,l[N<<5],r[N<<5],x,y,z,L,R,c[N<<6],Num,p[N<<6],a[N<<5],h[N],f[N],d[N],Size[N],Rt,Sum;
 26 bool b[N];
 27 struct G{
 28     int l,r,w,t;
 29     G(){}
 30     G(int l,int r,int w,int t):l(l),r(r),w(w),t(t){}
 31     bool operator<(G b)const{
 32         return a[t]+w<a[b.t]+b.w;
 33     }
 34 }tmp;
 35 priority_queue<G>q;
 36 inline void Add(int x,int y,int z){e[++Num].t=y;e[Num].w=z;e[Num].nx=h[x];h[x]=Num;}
 37 inline void Get_root(int x,int F){
 38     f[x]=0;Size[x]=1;
 39     for(int i=h[x];i;i=e[i].nx){
 40         int v=e[i].t;
 41         if(!b[v]&&v!=F){
 42             Get_root(v,x);
 43             Size[x]+=Size[v];
 44             f[x]=_Max(f[x],Size[v]);
 45         }
 46     }
 47     f[x]=_Max(f[x],Sum-Size[x]);
 48     if(f[x]<f[Rt])Rt=x;
 49 }
 50 inline void Dfs(int x,int F){
 51     a[++Cnt]=d[x];l[Cnt]=L;r[Cnt]=R;
 52     for(int i=h[x];i;i=e[i].nx){
 53         int v=e[i].t;
 54         if(!b[v]&&v!=F)d[v]=d[x]+e[i].w,Dfs(v,x);
 55     }
 56 }
 57 inline void Solve(int x){
 58     a[++Cnt]=0;d[x]=0;L=R=Cnt;b[x]=1;
 59     for(int i=h[x];i;i=e[i].nx){
 60         int v=e[i].t;
 61         if(!b[v])d[v]=e[i].w,Dfs(v,x),R=Cnt;
 62     }
 63     for(int i=h[x];i;i=e[i].nx){
 64         int v=e[i].t;
 65         if(!b[v]){
 66             f[Rt=0]=Size[v]+1;Sum=Size[v];
 67             Get_root(v,x);
 68             Solve(Rt);
 69         }
 70     }
 71 }
 72 inline void Build(int Node,int l,int r){
 73     if(l==r){
 74         c[Node]=a[l];
 75         p[Node]=l;
 76         return;
 77     }
 78     int Mid=l+r>>1;
 79     Build(Node<<1,l,Mid);
 80     Build(Node<<1|1,Mid+1,r);
 81     if(c[Node<<1]>c[Node<<1|1])c[Node]=c[Node<<1],p[Node]=p[Node<<1];else
 82     c[Node]=c[Node<<1|1],p[Node]=p[Node<<1|1];
 83 }
 84 inline int Query(int Node,int l,int r,int L,int R){
 85     if(l>R||r<L)return 0;
 86     if(l>=L&&r<=R)return Node;
 87     int Mid=l+r>>1;
 88     int Q1=Query(Node<<1,l,Mid,L,R),Q2=Query(Node<<1|1,Mid+1,r,L,R);
 89     return c[Q1]>c[Q2]?Q1:Q2;
 90 }
 91 int main()
 92 {
 93     Read(n);Read(m);
 94     for(i=1;i<n;i++)Read(x),Read(y),Read(z),Add(x,y,z),Add(y,x,z);
 95     f[Rt=0]=n+1;Sum=n;Get_root(1,0);
 96     Solve(Rt);
 97     Build(1,1,Cnt);c[0]=-INF;
 98     for(i=1;i<=Cnt;i++)if(l[i])q.push(G(l[i],r[i],a[i],p[Query(1,1,Cnt,l[i],r[i])]));
 99     while(m--){
100         tmp=q.top();q.pop();
101         printf("%d\n",tmp.w+a[tmp.t]);
102         if(tmp.t>tmp.l)q.push(G(tmp.l,tmp.t-1,tmp.w,p[Query(1,1,Cnt,tmp.l,tmp.t-1)]));
103         if(tmp.t<tmp.r)q.push(G(tmp.t+1,tmp.r,tmp.w,p[Query(1,1,Cnt,tmp.t+1,tmp.r)]));
104     }
105     return 0;
106 }

bzoj3784

时间: 2024-11-07 05:31:15

bzoj2006 [ NOI2010 ] && bzoj3784 --点分治+线段树+堆的相关文章

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

【BZOJ4372】烁烁的游戏 动态树分治+线段树

[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被烁烁吸引,所以会一直待在节点上不动.烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠.大意:给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. In

ACdream1157 Segments(CDQ分治 + 线段树)

题目这么说的: 进行如下3种类型操作:1)D L R(1 <= L <= R <= 1000000000) 增加一条线段[L,R]2)C i (1-base) 删除第i条增加的线段,保证每条插入线段最多插入一次,且这次删除操作一定合法3) Q L R(1 <= L <= R <= 1000000000) 查询目前存在的线段中有多少条线段完全包含[L,R]这个线段,线段X被线段Y完全包含即LY <= LX <= RX <= RY) 初学CDQ分治是看了B

BZOJ 4311: 向量( 按时间分治 + 线段树 )

离线, 然后按时间分治, 每个向量都有出现时间[l, r], 直接插入时间线段树(一个向量只会影响O(logN)数量级的线段树节点). 在线段树每个节点弄出凸壳然后二分. 时间复杂度O(Nlog^2N) --------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #includ

【BZOJ3730】震波 动态树分治+线段树

[BZOJ3730]震波 Description 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动.接下来你需要在线处理M次操作:0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和.1 x y 表示第x个城市的价值变成了y.为了体现程序的在

HDU 6183 Color it cdq分治 + 线段树 + 状态压缩

Color it Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Problem Description Do you like painting? Little D doesn't like painting, especially messy color paintings. Now Little B is painting. To prevent him from

hdu 4366 Successor - CDQ分治 - 线段树 - 树分块

Sean owns a company and he is the BOSS.The other Staff has one Superior.every staff has a loyalty and ability.Some times Sean will fire one staff.Then one of the fired man’s Subordinates will replace him whose ability is higher than him and has the h

【题解】Berland.Taxi Codeforces 883L 模拟 线段树 堆

Prelude 题目传送门:ヾ(?ω?`)o Solution 按照题意模拟即可. 维护一个优先队列,里面装的是正在运营中的出租车,关键字是乘客的下车时间. 维护一个线段树,第\(i\)个位置表示第\(i\)个房子前面有没有停放出租车,这样在有人需要打车的时候可以快速找到离她最近的车的位置. 对每个房子维护一个堆,里面装的是停在这个房子前面的出租车,关键字是出租车的编号和上一个乘客下车的时间,上一个乘客下车越早,等待时间越长. 然后模拟时间的流逝就可以了,代码非常好写. Code #includ

ZQC 的手办---------------线段树+堆

   • 看到题目基本上都会想到线段树维护吧.  • 但是询问看似比较棘手,其实题目相当于就相当于在要求在询问的时候需要准确的,依次的算出答案.难道不是吗?    • 在确定用线段树维护修改操作后,这是一种怎样的询问呢?-------------求一段区间的前 k 小的数,而且我们不能依赖区间长度!    • 然后,我们考虑在取出一个区间的最小值之后会产生什么:区间分裂了,总数减少了,有一个最值被确定了.这样一来,接下来的答   案就在剩下的区间里面了.     • 可以发现只要我们对一个区间保