bzoj 3123: [Sdoi2013]森林

如果题号没记错的话,2588是一个树上的主席树查询。这个题就是多了个合并而已。每一次都把小的合并到大的上就好了(所谓启发式2333)

(主席树真是个好东西2333)

(上部分为本蒟蒻不知道为什么RE到死都RE的代码,,,挖坑)

(个人感觉主席树这种东西里离散不离散没什么区别的(常数而已),毕竟是log的,大个几次方就是多了个常数而已)

  1 /*#include<bits/stdc++.h>
  2 #define N 100005
  3 #define M 40000005
  4 #define LL long long
  5 #define inf 0x3f3f3f3f
  6 using namespace std;
  7 inline int ra()
  8 {
  9     int x=0,f=1; char ch=getchar();
 10     while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
 11     while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
 12     return x*f;
 13 }
 14 int sz,n,m,Q,T,cnt,ans;
 15 int ls[M],rs[M],sum[M],root[N],head[N];
 16 int size[N],belong[N],fa[N][20],vis[N],deep[N],a[N],hash[N],num[N],tot;
 17 struct node{int to,next;}e[N<<2];
 18 void ins(int x, int y){e[++cnt].next=head[x]; e[cnt].to=y; head[x]=cnt;}
 19 int find(int x)
 20 {
 21     int l=1,r=tot;
 22     while (l<=r)
 23     {
 24         int mid=l+r>>1;
 25         if (hash[mid]==x) return mid;
 26         else if (hash[mid]<x) l=mid+1;
 27         else r=mid-1;
 28     }
 29 }
 30 void insert(int l, int r, int x, int &y, int v)
 31 {
 32     y=++sz;
 33     sum[y]=sum[x]+1;
 34     ls[y]=ls[x]; rs[y]=rs[x];
 35     if (l==r) return;
 36     int mid=l+r>>1;
 37     if (v<=mid) insert(l,mid,ls[x],ls[y],v);
 38     else insert(mid+1,r,rs[x],rs[y],v);
 39 }
 40 void dfs(int x, int bl)
 41 {
 42     for (int i=1; i<=16; i++)
 43         fa[x][i]=fa[fa[x][i-1]][i-1];
 44     insert(1,tot,root[fa[x][0]],root[x],find(a[x]));
 45     belong[x]=bl; size[belong[x]]++;
 46     for (int i=head[x];i;i=e[i].next)
 47     {
 48         if (e[i].to==fa[x][0]) continue;
 49         deep[e[i].to]=deep[x]+1;
 50         fa[e[i].to][0]=x;
 51         dfs(e[i].to,bl);
 52     }
 53 }
 54 int getlca(int x, int y)
 55 {
 56     if (deep[x]<deep[y]) swap(x,y);
 57     int t=deep[x]-deep[y];
 58     for (int i=0; i <=16; i++)
 59         if (t&(1<<i)) x=fa[x][i];
 60     for (int i=16; i>=0; i--)
 61         if (fa[x][i]!=fa[y][i])
 62             x=fa[x][i],y=fa[y][i];
 63     if (x==y) return x;
 64     return fa[x][0];
 65 }
 66 void query(int x, int y, int k)
 67 {
 68     int a1=root[x],b=root[y],z=getlca(x,y),w=a[z];
 69     int c=root[z];
 70     int l=1,r=tot;
 71     while (l!=r)
 72     {
 73         int mid=l+r>>1;
 74         int tmp=sum[ls[a1]]+sum[ls[b]]-2*sum[ls[c]]+(w>=l && w<=mid);
 75         if (tmp>=k) r=mid,a1=ls[a1],b=ls[b],c=ls[c];
 76         else k-=tmp,l=mid+1,a1=rs[a1],b=rs[b],c=rs[c];
 77     }
 78     printf("%d\n",ans=hash[l]);
 79 }
 80 void merge(int x, int y)
 81 {
 82     if (size[belong[x]]<size[belong[y]]) swap(x,y);
 83     fa[y][0]=x; deep[y]=deep[x]+1; ins(x,y); ins(y,x);
 84     dfs(y,belong[x]);
 85 }
 86 int main()
 87 {
 88     T=ra(); int ti=0;
 89     while (T--)
 90     {
 91         n=ra(); m=ra(); Q=ra();
 92         for (int i=1; i<=n; i++) num[i]=a[i]=ra();
 93         sort(num+1,num+n+1);
 94         hash[++tot]=num[1];
 95         for (int i=2; i<=n; i++)
 96             if (num[i]!=num[i-1])
 97                 hash[++tot]=num[i];
 98         for (int i=1; i<=m; i++)
 99         {
100             int x=ra(),y=ra();
101             ins(x,y); ins(y,x);
102         }
103         for (int i=1; i<=n; i++)
104             if  (!fa[i][0]) dfs(i,i);
105         for (int i=1; i<=Q; i++)
106         {
107             char s[3]; scanf("%s",s);
108             int x=ra()^ans,y=ra()^ans;
109             if (s[0]==‘Q‘)
110             {
111                 int k=ra()^ans;
112                 query(x,y,k);
113             }
114             else merge(x,y);
115         }
116     }
117     return 0;
118 }*/
119
120
121
122
123
124
125
126
127
128 #include<bits/stdc++.h>
129 #define N 100005
130 #define M 10000005
131 #define LL long long
132 #define inf 0x3f3f3f3f
133 using namespace std;
134 inline int ra()
135 {
136     int x=0,f=1; char ch=getchar();
137     while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
138     while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
139     return x*f;
140 }
141 int n,m,T,ans,cnt,tot,sz,testcase,query_sum;
142 int f[N],size[N],deep[N],a[N],num[N],hash[N],root[N],fa[N][17];
143 int ls[M],rs[M],sum[M];
144 int head[N],list[N<<1],next[N<<1];
145 struct node{
146     int to,next;
147 }e[N<<1];
148 int Find(int x)
149 {
150     int l=1,r=tot;
151     while (l<=r)
152     {
153         int mid=l+r>>1;
154         if (hash[mid]==x) return mid;
155         else if (hash[mid]<x) l=mid+1;
156         else r=mid-1;
157     }
158 }
159 void insert(int x, int y)
160 {
161     e[++cnt].next=head[x]; e[cnt].to=y; head[x]=cnt;
162 }
163 int find(int i)
164 {
165     if (!f[i]) size[i]=1,f[i]=i;
166     if (f[i]==i) return i;
167     return f[i]=find(f[i]);
168 }
169 void merge(int x, int y)
170 {
171     int p=find(x),q=find(y);
172     f[p]=q; size[q]+=size[p];
173 }
174 void update(int l, int r, int x, int &y, int v)
175 {
176     y=++sz;
177     ls[y]=ls[x]; rs[y]=rs[x]; sum[y]=sum[x]+1;
178     if (l==r) return;
179     int mid=l+r>>1;
180     if (v<=mid) update(l,mid,ls[x],ls[y],v);
181     else update(mid+1,r,rs[x],rs[y],v);
182 }
183 void dfs(int x)
184 {
185     for (int i=1; i<=16; i++)
186         fa[x][i]=fa[fa[x][i-1]][i-1];
187     update(1,tot,root[fa[x][0]],root[x],Find(a[x]));
188     for (int i=head[x];i;i=e[i].next)
189     {
190         if (e[i].to==fa[x][0]) continue;
191         deep[e[i].to]=deep[x]+1;
192         fa[e[i].to][0]=x;
193         dfs(e[i].to);
194     }
195 }
196 int lca(int x, int y)
197 {
198     if (deep[x]<deep[y]) swap(x,y);
199     int t=deep[x]-deep[y];
200     for (int i=0; (1<<i)<=t; i++)
201         if ((1<<i)&t) x=fa[x][i];
202     for (int i=16; i>=0; i--)
203         if (fa[x][i]!=fa[y][i])
204             x=fa[x][i],y=fa[y][i];
205     return x==y?x:fa[x][0];
206 }
207 int query(int a, int b, int c, int d, int k)
208 {
209     int l=1,r=tot;
210     while (l!=r)
211     {
212         int mid=l+r>>1;
213         int tmp=sum[ls[a]]+sum[ls[b]]-sum[ls[c]]-sum[ls[d]];
214         if (tmp>=k) r=mid,a=ls[a],b=ls[b],c=ls[c],d=ls[d];
215         else l=mid+1,k-=tmp,a=rs[a],b=rs[b],c=rs[c],d=rs[d];
216     }
217     return l;
218 }
219 int main()
220 {
221     int textcast=ra();
222     n=ra(); m=ra(); T=ra();
223     for (int i=1; i<=n; i++) a[i]=num[i]=ra();
224     sort(num+1,num+n+1);
225     hash[++tot]=num[1];
226     for (int i=2; i<=n; i++)
227         if (num[i]!=num[i-1]) hash[++tot]=num[i];
228     for (int i=1; i<=m; i++)
229     {
230         int x=ra(),y=ra();
231         insert(x,y); insert(y,x); merge(x,y);
232     }
233     for (int i=1; i<=n; i++)
234         if (!fa[i][0]) dfs(i);
235     while (T--)
236     {
237         char s[3]; scanf("%s",s);
238         int a=ra()^ans,b=ra()^ans;
239         if (s[0]==‘Q‘)
240         {
241             int k=ra()^ans;
242             int c=lca(a,b);
243             int d=fa[c][0];
244             printf("%d\n",ans=hash[query(root[a],root[b],root[c],root[d],k)]);
245         }
246         else
247         {
248             int p=find(a),q=find(b);
249             if (size[p]>size[q]) swap(a,b);
250             fa[a][0]=b; deep[a]=deep[b]+1;
251             dfs(a);
252             merge(a,b);
253             insert(a,b); insert(b,a);
254         }
255     }
256     return 0;
257 }
时间: 2024-07-28 22:02:22

bzoj 3123: [Sdoi2013]森林的相关文章

BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并

题目大意:给定一棵森林,每个点有权值,提供两种操作: 1.查询两点间路径上第k小的权值 2.将两个点之间连一条边 保证连接后仍是一座森林 可持久化线段树部分同Count On A Tree 只是这道题加了个连接操作 对于连接操作我们要用到启发式合并 就是把小的那棵树暴力重建 很简单的一个操作但是可以证明是均摊O(nlogn)的 大小我用了并查集 其实记录根就可以了 此外本题的多组数据是在逗比 记住testcase恒等于1就是了 NND我倍增LCA又写错了0.0 预处理时居然从大往小写的0.0 样

bzoj 3198: [Sdoi2013]spring 题解

[原题] 3198: [Sdoi2013]spring Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 253  Solved: 95 Description Input Output Sample Input 3 3 1 2 3 4 5 6 1 2 3 0 0 0 0 0 0 4 5 6 Sample Output 2 HINT [题解]这道题明明是水题,坑了我两天!!!真是伤心.发现哈希都不熟练了. 首先很容易想到是2^6枚举01状态,使得1

Luogu_P3302 [SDOI2013]森林【题解】主席树 lca 启发式合并

# Luogu_P3302 [SDOI2013]森林 主席树,启发式合并,lca luogu题面 求树上路径的第k大,树之间还有合并. 明显是主席树再加合并. 先说链上第k大,其实就是$Tx+Ty-Tlca-Tlcafa$ $T$表示权值线段树. 主席树维护的是从根节点到当前节点的前缀和. ask的代码如下: inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){ if(l==r) return b[l]; int lz=su

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,

BZOJ 3123 【SDOI2013】 森林

题目链接:森林 这道题想法很显然.既然只有加边而没有删边,那么每次启发式合并就可以了.查询路径\(k\)小似乎需要主席树,那么把主席树和倍增表一起暴力重构就好了. 然后发现这样的空间复杂度是\(O(n\log^2n)\)的.感觉非常不靠谱,于是滚去写了个节点回收站--然后发现主席树节点回收的话每个节点会被\(dfs\)到多次,还需要一些特判-- 然后就是一直\(RE\)--由于题目是强制在线的那么就应该是\(Wa\)了.但是对拍了\(6W\)组数据没出错我还能说什么呢-- 最后突然发现,我的倍增

【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时间复杂度为O(n*log),因为每个的节点只会作为小的部分合并,因此他所在的一小部分至少变大2倍,对于每一个节点他作为被合并方最多log次,因此其复杂度为O(n*log),而这个是绝对跑不满还差很多的,我们视他为无常数就好了,当然这一切都是建立在无拆分操作的基础之上,只要有了拆分启发式合并的复杂度就

BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并

题目大意:给出一个森林,每个节点都有一个权值.有若干加边操作,问两点之间路径上的第k小权值是多少. 思路:这题和COT1比较像,但是多了连接操作.这样就只能暴力合并连个树.启发式合并会保证时间复杂度不至于太大.然后就是用可持久化线段树维护一个树的信息,按照dfs序来建树,每个节点的可持久化链的参考版本就是它父亲的版本.之后利用权值线段树可区间加减的特性,用f[x] + f[y] - f[lca] - f[father[lca]]来计算权值. CODE: #include <cstdio> #i

[Sdoi2013]森林

/* 平常这种题很常见的思路就是求出dfs序来,然后每次查询的时候就是在主席树上查询 x+y-lca-fa[lca] 的值就行了. 但是这个题要动态的给森林中加边,还是强制在线的,所以就需要考虑换一种方法来维护这个东西. 首先先dfs出每棵树来,然后对于link操作,可以启发式合并两个主席树.这里我们把主席树维护的dfs序变成维护每个点到根的这条路径.所里link的时候假设我们要把x合到y上,那么我们就边dfs x 这棵树,边用当前点的fa作为历史状态的root来更新当前点的root就行了.求l

[BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】

题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 m 减去 Ai - 1 ,相当于将这一部分固定分给 xi,就转化为无限制的情况了. 如果有一些限制条件是 xi <= Ai 呢?直接来求就不行了,但是注意到这样的限制不超过 8 个,我们可以使用容斥原理来求. 考虑容斥:考虑哪些限制条件被违反了,也就是说,有哪些限制为 xi <= Ai 却是 xi