#LOJ2564 SDOI2018 原题识别 主席树

转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/9057297.html

原题链接:

今天考试考了前天的SDOI考题

天啊我菜爆,只有T2拿了30分

然后考试后半程一直在打T1

觉得考试思路很有意思,于是就顺着打下来了

个人感觉这个是$O(nlogn^{2})$的,但是在loj上我比claris的程序快了1s多,只不过编程复杂度不止翻倍啊……

下面介绍一下我的解法

其实最早启发我的是链上的部分分

定义$pre_{i}$为i前面最近的和i同色的点的下标,我们把每个点的pre看成权值,插入一个主席树

这样的话,1操作我们只需要用主席树查$[L,R]$区间内pre<L的点数即可

然后我们考虑2操作,我的思路是统计每个点的贡献

对于这两个询问点,我们假设深度较浅的点为A,较深的为B

我们分B选的点在A前面还是后面来讨论

对于点i>A,那么点i的贡献应该是$[pre_{i}<=A]*(A-pre_{i})*(B-i+1)$

这个很好理解,前面布尔表达式是做贡献的前提,后面两个括号一个是左端点A的区间,一个是右端点B的区间

而对于i<=A,式子是类似的:贡献是$(i-pre[i])*(A-i+1+B-i+1)-1$

这里后面减1是因为A,B同时选择i点的情况被贡献了2次,所以要减一下

请选手手动把上面的括号都乘开……然后……

我们需要维护下面4个变量:$i$的和,$pre_{i}$的和,$i^{2}$的和,以及$i*pre_{i}$的和

由于空间有限,这里不展开写式子了

而当问题扩展到树上的时候……

我们先把刚才那个pre搬到树上来,

并且把pre的定义变成同色的最近祖先的深度

并且把编号全部改成深度

这两点非常重要,我在调试的时候因为有几处没有把原来序列上的编号改成深度调了半天……

然后把那个主席树也搬上来,维护从i到根的pre信息,这个两个操作都要用

再以颜色为下标建一个树上主席树,$root_{i}$维护i到根的颜色,这个我们1操作要用

我们发现这是个随机树

那么每个点距离链的深度应该会非常小

先考虑1操作,我们找到两个点的lca,这个可以通过直接暴力爬父亲直到走到链上解决,复杂度$O(logn)$

然后在维护pre的树上查询$pre<deep[lca]$的点的个数

再暴力爬深度较浅的那棵树,爬到lca,查询$lca---r$那条链里是否有这种颜色,以及这次暴力爬是否已经经过了这个元素

然后就得到了答案

这个其实我说的麻烦……可以参考一下代码,挺简单的

然后对于2操作呢……

我们还是分开考虑,具体来说我是这样搞得:

A选择$1-----lca$,B随意选择$1---r$,这样使用刚才链的算法可以统计

A选择$lca----l$,B选择$1----lca$,我们统计B选择的点和A选择的点,这个把刚才的式子稍微修改一下就能统计

具体来说,设$l----lca$的长度是L,那么A和B的贡献分别是

$(lca-pre_{i})*(deep[l]-deep[i]+1)$和

$(i-pre_{i})*l$

然后我们再考虑一种最难处理的情况:A在$lca---l$,B在$lca---r$

然后我们用和刚才类似的方法可以算B的贡献:$[pre_{i} < deep_{lca}]*(B-i+1)*l$

然后A的贡献的话,由于颜色也是随机的所以每种颜色个数比较少,我们可以暴力找到$lca---r$上第一个同色点然后算贡献

语言表述可能很让人难以理解……

请参考下面namespace sgt1和deal1&deal2函数来更好的理解上面的式子……

这样的话,我们的复杂度是$nlogn$*期望距链距离 + $nlogn$ + $n$*期望颜色个数*期望距链深度……比较玄学吧……

下面附上代码,保留了一定注释增强观赏体验……

  1 #pragma GCC optimize("O3")
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <algorithm>
  6 using namespace std;
  7 #define RG register
  8 #define UI unsigned int
  9 #define LL long long
 10 #define N 100010
 11 UI SA,SB,SC;
 12 inline UI make()
 13 {
 14     SA^=SA<<16;SA^=SA>>5;SA^=SA<<1;
 15     UI tmp=SA;
 16     SA=SB,SB=SC,SC^=SA^tmp;
 17     return SC;
 18 }
 19 int n,col[N],m,q,e,adj[N];
 20 struct edge{int zhong,next;}s[N<<1];
 21 inline void add(int qi,int zhong)
 22     {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
 23 int fa[N],lst[N],pre[N],deep[N];
 24 LL s1,s2,s3;int sz;
 25 namespace sgt2
 26 {
 27     struct node
 28     {
 29         node *ch[2];int size;
 30     }*root[N],*null,mem[N<<5];int tot;
 31     inline void init()
 32     {
 33         root[0]=null=new node();
 34         null->ch[0]=null->ch[1]=null;
 35     }
 36     inline void ins(node *&o,node *old,int l,int r,int pos)
 37     {
 38         o=mem+(tot++);o->size=old->size+1;
 39         if(l==r)return;RG int mi=l+r>>1;
 40         if(pos<=mi)o->ch[1]=old->ch[1],ins(o->ch[0],old->ch[0],l,mi,pos);
 41         else o->ch[0]=old->ch[0],ins(o->ch[1],old->ch[1],mi+1,r,pos);
 42     }
 43     inline void ins(int x,int y){ins(root[x],root[y],1,n,col[x]);}
 44     inline int querysz(node *a,node *b,int l,int r,int x)
 45     {
 46         if(l==r)return b->size - a->size;
 47         RG int mi=l+r>>1;
 48         if(x<=mi)return querysz(a->ch[0],b->ch[0],l,mi,x);
 49         return querysz(a->ch[1],b->ch[1],mi+1,r,x);
 50     }
 51     inline bool nope(int l,int r,int x)
 52         {return querysz(root[fa[l]],root[r],1,n,x)==0;}
 53 }
 54 namespace sgt1
 55 {
 56     struct node
 57     {
 58         node *ch[2];int size;
 59         LL sum1,sum2,sum3;
 60     }*root[N],*null,mem[N<<5];int tot;
 61     inline LL S(int r){return r*(r+1ll)/2;}
 62     inline LL S2(int x){return (LL)x*(x+1)*(2*x+1)/6;}
 63     inline void ins(node *&o,node *old,int l,int r,int pos,int pr)
 64     {
 65         o=mem+(tot++);
 66         o->size=old->size+1;o->sum1=old->sum1+pos;
 67         o->sum2=old->sum2+pr;o->sum3=old->sum3+(LL)pos*pr;
 68         if(l==r)return;
 69         RG int mi=l+r>>1;
 70         if(pr<=mi)o->ch[1]=old->ch[1],ins(o->ch[0],old->ch[0],l,mi,pos,pr);
 71         else o->ch[0]=old->ch[0],ins(o->ch[1],old->ch[1],mi+1,r,pos,pr);
 72     }
 73     inline int querysz(node *a,node *b,int l,int r,int R)
 74     {
 75         if(l==r)return b->size - a->size;
 76         RG int mi=l+r>>1;
 77         if(mi<R)return b->ch[0]->size - a->ch[0]->size + querysz(a->ch[1],b->ch[1],mi+1,r,R);
 78         return querysz(a->ch[0],b->ch[0],l,mi,R);
 79     }
 80     inline void query(node *a,node *b,int l,int r,int R)
 81     {
 82         if(l==r)
 83         {
 84             sz+=b->size - a->size;s1+=b->sum1 - a->sum1;
 85             s2+=b->sum2 - a->sum2;s3+=b->sum3 - a->sum3;
 86             return;
 87         }
 88         RG int mi=l+r>>1;
 89         if(mi<R)
 90         {
 91             sz+=b->ch[0]->size - a->ch[0]->size;s1+=b->ch[0]->sum1 - a->ch[0]->sum1;
 92             s2+=b->ch[0]->sum2 - a->ch[0]->sum2;s3+=b->ch[0]->sum3 - a->ch[0]->sum3;
 93             query(a->ch[1],b->ch[1],mi+1,r,R);
 94         }
 95         else query(a->ch[0],b->ch[0],l,mi,R);
 96     }
 97     inline int deal1(int l,int r)
 98         {return querysz(root[fa[l]],root[r],0,n-1,deep[fa[l]]);}
 99     inline LL q1(int A,int B)
100     {
101         if(A==B)return 0;
102         sz=s1=s2=s3=0;query(root[A],root[B],0,n-1,A);
103         A=deep[A],B=deep[B];
104         return (LL)sz*A*B + (sz-s1)*A -s2*(B+1) + s3;//s1还是应该维护,这个地方没法算
105     }
106     inline LL exq1(int A,int B,int l)
107     {
108         if(A==B)return 0;
109         sz=s1=s2=s3=0;query(root[fa[A]],root[B],0,n-1,fa[A]);
110         //这里应该是要去fa[A]那里,lca那个地方也包含在区间里面,并且lca不能作为B的落点被算进去……?(lca之前在B侧的1--lca被算了)
111         return ( sz*(deep[B]+1ll) - s1 )*l -l;
112     }
113     inline LL q2(int A,int B)
114         {return ( root[A]->sum3 - S2(deep[A]) )*2 + (deep[A]+deep[B]+2ll)*( S(deep[A])-root[A]->sum2 ) - deep[A];}
115     inline LL exq2(int A,int l)
116         {return ( S(deep[A])-root[A]->sum2 ) * l;}
117     inline LL deal2(int l,int r)
118         {return ( (l<r)?q1(l,r):0 )+q2(l,r);}
119     inline void work()
120         {for(RG int i=1;i<=n;++i)ins(root[i],root[i-1],0,n-1,i,pre[i]);}
121     inline void init()
122         {root[0]=null=new node();null->ch[0]=null->ch[1]=null;}
123     inline void ins(int x,int y)
124         {ins(root[x],root[y],0,n-1,deep[x],pre[x]);}
125 }
126 int dfl[N],dfr[N],num;
127 inline void dfs(int rt)
128 {
129     dfl[rt]=++num;
130     RG int i,cur=lst[col[rt]];
131     pre[rt]=lst[col[rt]];lst[col[rt]]=deep[rt];
132     sgt1::ins(rt,fa[rt]);sgt2::ins(rt,fa[rt]);
133     for(i=adj[rt];i;i=s[i].next)dfs(s[i].zhong);
134     lst[col[rt]]=cur;
135     dfr[rt]=num;
136 }
137 int vis[N],T;
138 inline int deal1(int l,int r)
139 {
140     ++T;
141     RG int fa1=l,fa2=r,ret=0;
142     while(fa1>m)fa1=fa[fa1];
143     while(fa2>m)fa2=fa[fa2];
144     if(fa1==fa2)
145     {
146         while(l^r)
147             if(deep[l] > deep[r])
148                 {if(vis[col[l]]!=T)vis[col[l]]=T,++ret;l=fa[l];}
149             else
150                 {if(vis[col[r]]!=T)vis[col[r]]=T,++ret;r=fa[r];}
151         if(vis[col[l]]!=T)vis[col[l]]=T,++ret;
152         return ret;
153     }
154     if(fa1 > fa2)l^=r,r^=l,l^=r,fa1^=fa2,fa2^=fa1,fa1^=fa2;
155     ret=sgt1::deal1(fa1,r);
156     while(l>fa1)
157     {
158         if( vis[col[l]]!=T && sgt2::nope(fa1,r,col[l]) )
159             vis[col[l]]=T,++ret;
160         l=fa[l];
161     }
162     return ret;
163 }
164 int sta[30],cnt;
165 #include <vector>
166 vector<int>cont[N];
167 inline int calc(int pos,int lca,int r,int x)
168 {
169     if(col[lca]==x)return 0;
170     int ret=deep[r];
171     for(vector<int>::iterator it=cont[x].begin();it!=cont[x].end();++it)
172         if(deep[*it] >=deep[lca] && dfl[*it]<=dfl[r] && dfr[r] <=dfr[*it])
173             ret=min(ret,deep[*it]-1);
174     return ret-deep[lca];
175 }
176 inline LL deal2(int l,int r)
177 {
178     ++T;
179     RG int fa1=l,fa2=r,lca;
180     LL ret=0;
181     while(fa1>m)fa1=fa[fa1];
182     while(fa2>m)fa2=fa[fa2];
183     if(fa1==fa2)
184     {
185         for(fa1=l,fa2=r;l^r;)
186             if(deep[l] > deep[r])l=fa[l];else r=fa[r];
187         lca=l;l=fa1,r=fa2;
188     }
189     else
190     {
191         if(fa1 > fa2)l^=r,r^=l,l^=r,fa1^=fa2,fa2^=fa1,fa1^=fa2;
192         lca=fa1;
193     }
194     int l1=deep[l]-deep[lca];
195     ret=sgt1::deal2(lca,r) //A在1---r all
196     + sgt1::q1(lca,l) //A在lca---l,B在1--lca A侧的贡献
197     + sgt1::exq2(lca,l1) //A在lca---l,B在1--lca B侧的贡献
198     + sgt1::exq1(lca,r,l1);//A在lca---l,B在lca--r B侧的贡献
199     cnt=0;
200     fa1=l;
201     while(l>lca)sta[++cnt]=l,l=fa[l];
202     for(RG int i=cnt;i;--i)
203     {
204         l=sta[i];
205         if(vis[col[l]]!=T)
206             vis[col[l]]=T,ret+=(LL)calc(l,lca,r,col[l])*(deep[fa1]-deep[l]+1);
207     }
208     return ret;
209 }
210 int main()
211 {
212     RG int i,j,t,l,r,opt;
213     scanf("%d",&t);
214     sgt1::init();
215     sgt2::init();
216     while(t--)
217     {
218         scanf("%d%d%u%u%u",&n,&m,&SA,&SB,&SC);
219         for(i=2;i<=m;++i)fa[i]=i-1;
220         for(i=m+1;i<=n;++i)fa[i]=make()%(i-1)+1;
221         for(i=1;i<=n;++i)col[i]=make()%n+1;
222         for(i=1;i<=n;++i)cont[i].clear();
223         for(i=1;i<=n;++i)cont[col[i]].push_back(i);
224         memset(lst,0,n+1<<2);
225         num=0;
226         if(m==n)
227         {
228             for(i=1;i<=n;++i)
229                 pre[i]=lst[col[i]],lst[col[i]]=i;
230             sgt1::tot=0;
231             sgt1::work();
232             scanf("%d",&q);
233             for(i=1;i<=n;++i)deep[i]=i;
234             for(i=1;i<=q;++i)
235             {
236                 scanf("%d%d%d",&opt,&l,&r);
237                 if(l>r)l^=r,r^=l,l^=r;
238                 if(opt==1)printf("%d\n",sgt1::deal1(l,r));
239                 else printf("%lld\n",sgt1::deal2(l,r));
240             }
241         }
242         else
243         {
244             e=0;memset(adj,0,sizeof(adj));
245             for(i=1;i<=n;++i)deep[i]=deep[fa[i]]+1;
246             for(i=2;i<=n;++i)add(fa[i],i);
247             sgt1::tot=sgt2::tot=0;dfs(1);
248             scanf("%d",&q);
249             for(i=1;i<=q;++i)
250             {
251                 scanf("%d%d%d",&opt,&l,&r);
252                 if(l>r)l^=r,r^=l,l^=r;
253                 if(opt==1)printf("%d\n",deal1(l,r));
254                 else printf("%lld\n",deal2(l,r));
255             }
256         }
257     }
258 }

LOJ2564

原文地址:https://www.cnblogs.com/LadyLex/p/9057297.html

时间: 2024-10-10 09:33:14

#LOJ2564 SDOI2018 原题识别 主席树的相关文章

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

(中等) Hiho 1232 Couple Trees(15年北京网络赛F题),主席树+树链剖分。

"Couple Trees" are two trees, a husband tree and a wife tree. They are named because they look like a couple leaning on each other. They share a same root, and their branches are intertwined. In China, many lovers go to the couple trees. Under t

SDOI2018:原题识别

题解: https://files.cnblogs.com/files/clrs97/old-solution.pdf Code: #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; typedef long long ll; const int N=100010,M=1010,E=5500000,BUF=10000000,OUT=10000000; unsigned i

sdut 2610:Boring Counting(第四届山东省省赛原题,划分树 + 二分)

Boring Counting Time Limit: 3000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries, for each quer

HDU4605---Magic Ball Game(主席树 好题)

题意:一颗二叉树,任意节点要么有两个孩子要么没孩子. 然后有一个球,从结点1开始往子孙结点走. 每碰到一个结点,有三种情况 如果此球重量等于该结点重量,球就停下了 如果此球重量小于该结点重量,则分别往左右儿子走的可能都是1/2 如果此球重量大于该结点重量,则走向左儿子的概率是1/8,右儿子的概率是7/8 然后若干个询问(10^5次),问一个重量为x的球经过结点v的概率 观察路径,可以发现路径可以分成两种,向左走的路径和向右走的路径,分成这两种是因为各自的计算公式,在向左走的路径中,设大于x的点权

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

hdu 5919 主席树入门题

主席树是从右往左初始化,每次把这个数出现过的位置消去,然后在当前位置加一. 然后我的做法是查两遍,第一遍能找出不同的个数,除一半:再用这个值查,一直到底,最后返回位置,比较套路的一题. #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include &l

【POJ 2104】 K-th Number 主席树模板题

达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没有评测,但我立下flag这个代码一定能A.我的同学在自习课上考语文,然而机房党都跑到机房来避难了\(^o^)/~ #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(i

POJ 2104 求序列里第K大 主席树裸体题

给定一个n的序列,有m个询问 每次询问求l-r 里面第k大的数字是什么 只有询问,没有修改 可以用归并树和划分树(我都没学过..囧) 我是专门冲着弄主席树来的 对主席树的建树方式有点了解了,不过这题为什么是在主席树里面这么操作的 还是有点不懂,今天照着模板敲了一遍就打多校了 再研究吧 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using name