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

原题链接:

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

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

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

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

个人感觉这个是$O(nlog^{2}n)$的,但是在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$*期望颜色个数*期望距链深度……比较玄学吧……

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

 #pragma GCC optimize("O3")
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define RG register
#define UI unsigned int
#define LL long long
#define N 100010
UI SA,SB,SC;
inline UI make()
{
SA^=SA<<;SA^=SA>>;SA^=SA<<;
UI tmp=SA;
SA=SB,SB=SC,SC^=SA^tmp;
return SC;
}
int n,col[N],m,q,e,adj[N];
struct edge{int zhong,next;}s[N<<];
inline void add(int qi,int zhong)
{s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
int fa[N],lst[N],pre[N],deep[N];
LL s1,s2,s3;int sz;
namespace sgt2
{
struct node
{
node *ch[];int size;
}*root[N],*null,mem[N<<];int tot;
inline void init()
{
root[]=null=new node();
null->ch[]=null->ch[]=null;
}
inline void ins(node *&o,node *old,int l,int r,int pos)
{
o=mem+(tot++);o->size=old->size+;
if(l==r)return;RG int mi=l+r>>;
if(pos<=mi)o->ch[]=old->ch[],ins(o->ch[],old->ch[],l,mi,pos);
else o->ch[]=old->ch[],ins(o->ch[],old->ch[],mi+,r,pos);
}
inline void ins(int x,int y){ins(root[x],root[y],,n,col[x]);}
inline int querysz(node *a,node *b,int l,int r,int x)
{
if(l==r)return b->size - a->size;
RG int mi=l+r>>;
if(x<=mi)return querysz(a->ch[],b->ch[],l,mi,x);
return querysz(a->ch[],b->ch[],mi+,r,x);
}
inline bool nope(int l,int r,int x)
{return querysz(root[fa[l]],root[r],,n,x)==;}
}
namespace sgt1
{
struct node
{
node *ch[];int size;
LL sum1,sum2,sum3;
}*root[N],*null,mem[N<<];int tot;
inline LL S(int r){return r*(r+1ll)/;}
inline LL S2(int x){return (LL)x*(x+)*(*x+)/;}
inline void ins(node *&o,node *old,int l,int r,int pos,int pr)
{
o=mem+(tot++);
o->size=old->size+;o->sum1=old->sum1+pos;
o->sum2=old->sum2+pr;o->sum3=old->sum3+(LL)pos*pr;
if(l==r)return;
RG int mi=l+r>>;
if(pr<=mi)o->ch[]=old->ch[],ins(o->ch[],old->ch[],l,mi,pos,pr);
else o->ch[]=old->ch[],ins(o->ch[],old->ch[],mi+,r,pos,pr);
}
inline int querysz(node *a,node *b,int l,int r,int R)
{
if(l==r)return b->size - a->size;
RG int mi=l+r>>;
if(mi<R)return b->ch[]->size - a->ch[]->size + querysz(a->ch[],b->ch[],mi+,r,R);
return querysz(a->ch[],b->ch[],l,mi,R);
}
inline void query(node *a,node *b,int l,int r,int R)
{
if(l==r)
{
sz+=b->size - a->size;s1+=b->sum1 - a->sum1;
s2+=b->sum2 - a->sum2;s3+=b->sum3 - a->sum3;
return;
}
RG int mi=l+r>>;
if(mi<R)
{
sz+=b->ch[]->size - a->ch[]->size;s1+=b->ch[]->sum1 - a->ch[]->sum1;
s2+=b->ch[]->sum2 - a->ch[]->sum2;s3+=b->ch[]->sum3 - a->ch[]->sum3;
query(a->ch[],b->ch[],mi+,r,R);
}
else query(a->ch[],b->ch[],l,mi,R);
}
inline int deal1(int l,int r)
{return querysz(root[fa[l]],root[r],,n-,deep[fa[l]]);}
inline LL q1(int A,int B)
{
if(A==B)return ;
sz=s1=s2=s3=;query(root[A],root[B],,n-,A);
A=deep[A],B=deep[B];
return (LL)sz*A*B + (sz-s1)*A -s2*(B+) + s3;//s1还是应该维护,这个地方没法算
}
inline LL exq1(int A,int B,int l)
{
if(A==B)return ;
sz=s1=s2=s3=;query(root[fa[A]],root[B],,n-,fa[A]);
//这里应该是要去fa[A]那里,lca那个地方也包含在区间里面,并且lca不能作为B的落点被算进去……?(lca之前在B侧的1--lca被算了)
return ( sz*(deep[B]+1ll) - s1 )*l -l;
}
inline LL q2(int A,int B)
{return ( root[A]->sum3 - S2(deep[A]) )* + (deep[A]+deep[B]+2ll)*( S(deep[A])-root[A]->sum2 ) - deep[A];}
inline LL exq2(int A,int l)
{return ( S(deep[A])-root[A]->sum2 ) * l;}
inline LL deal2(int l,int r)
{return ( (l<r)?q1(l,r): )+q2(l,r);}
inline void work()
{for(RG int i=;i<=n;++i)ins(root[i],root[i-],,n-,i,pre[i]);}
inline void init()
{root[]=null=new node();null->ch[]=null->ch[]=null;}
inline void ins(int x,int y)
{ins(root[x],root[y],,n-,deep[x],pre[x]);}
}
int dfl[N],dfr[N],num;
inline void dfs(int rt)
{
dfl[rt]=++num;
RG int i,cur=lst[col[rt]];
pre[rt]=lst[col[rt]];lst[col[rt]]=deep[rt];
sgt1::ins(rt,fa[rt]);sgt2::ins(rt,fa[rt]);
for(i=adj[rt];i;i=s[i].next)dfs(s[i].zhong);
lst[col[rt]]=cur;
dfr[rt]=num;
}
int vis[N],T;
inline int deal1(int l,int r)
{
++T;
RG int fa1=l,fa2=r,ret=;
while(fa1>m)fa1=fa[fa1];
while(fa2>m)fa2=fa[fa2];
if(fa1==fa2)
{
while(l^r)
if(deep[l] > deep[r])
{if(vis[col[l]]!=T)vis[col[l]]=T,++ret;l=fa[l];}
else
{if(vis[col[r]]!=T)vis[col[r]]=T,++ret;r=fa[r];}
if(vis[col[l]]!=T)vis[col[l]]=T,++ret;
return ret;
}
if(fa1 > fa2)l^=r,r^=l,l^=r,fa1^=fa2,fa2^=fa1,fa1^=fa2;
ret=sgt1::deal1(fa1,r);
while(l>fa1)
{
if( vis[col[l]]!=T && sgt2::nope(fa1,r,col[l]) )
vis[col[l]]=T,++ret;
l=fa[l];
}
return ret;
}
int sta[],cnt;
#include <vector>
vector<int>cont[N];
inline int calc(int pos,int lca,int r,int x)
{
if(col[lca]==x)return ;
int ret=deep[r];
for(vector<int>::iterator it=cont[x].begin();it!=cont[x].end();++it)
if(deep[*it] >=deep[lca] && dfl[*it]<=dfl[r] && dfr[r] <=dfr[*it])
ret=min(ret,deep[*it]-);
return ret-deep[lca];
}
inline LL deal2(int l,int r)
{
++T;
RG int fa1=l,fa2=r,lca;
LL ret=;
while(fa1>m)fa1=fa[fa1];
while(fa2>m)fa2=fa[fa2];
if(fa1==fa2)
{
for(fa1=l,fa2=r;l^r;)
if(deep[l] > deep[r])l=fa[l];else r=fa[r];
lca=l;l=fa1,r=fa2;
}
else
{
if(fa1 > fa2)l^=r,r^=l,l^=r,fa1^=fa2,fa2^=fa1,fa1^=fa2;
lca=fa1;
}
int l1=deep[l]-deep[lca];
ret=sgt1::deal2(lca,r) //A在1---r all
+ sgt1::q1(lca,l) //A在lca---l,B在1--lca A侧的贡献
+ sgt1::exq2(lca,l1) //A在lca---l,B在1--lca B侧的贡献
+ sgt1::exq1(lca,r,l1);//A在lca---l,B在lca--r B侧的贡献
cnt=;
fa1=l;
while(l>lca)sta[++cnt]=l,l=fa[l];
for(RG int i=cnt;i;--i)
{
l=sta[i];
if(vis[col[l]]!=T)
vis[col[l]]=T,ret+=(LL)calc(l,lca,r,col[l])*(deep[fa1]-deep[l]+);
}
return ret;
}
int main()
{
RG int i,j,t,l,r,opt;
scanf("%d",&t);
sgt1::init();
sgt2::init();
while(t--)
{
scanf("%d%d%u%u%u",&n,&m,&SA,&SB,&SC);
for(i=;i<=m;++i)fa[i]=i-;
for(i=m+;i<=n;++i)fa[i]=make()%(i-)+;
for(i=;i<=n;++i)col[i]=make()%n+;
for(i=;i<=n;++i)cont[i].clear();
for(i=;i<=n;++i)cont[col[i]].push_back(i);
memset(lst,,n+<<);
num=;
if(m==n)
{
for(i=;i<=n;++i)
pre[i]=lst[col[i]],lst[col[i]]=i;
sgt1::tot=;
sgt1::work();
scanf("%d",&q);
for(i=;i<=n;++i)deep[i]=i;
for(i=;i<=q;++i)
{
scanf("%d%d%d",&opt,&l,&r);
if(l>r)l^=r,r^=l,l^=r;
if(opt==)printf("%d\n",sgt1::deal1(l,r));
else printf("%lld\n",sgt1::deal2(l,r));
}
}
else
{
e=;memset(adj,,sizeof(adj));
for(i=;i<=n;++i)deep[i]=deep[fa[i]]+;
for(i=;i<=n;++i)add(fa[i],i);
sgt1::tot=sgt2::tot=;dfs();
scanf("%d",&q);
for(i=;i<=q;++i)
{
scanf("%d%d%d",&opt,&l,&r);
if(l>r)l^=r,r^=l,l^=r;
if(opt==)printf("%d\n",deal1(l,r));
else printf("%lld\n",deal2(l,r));
}
}
}
}

LOJ2564

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

  1. [SDOI2018]原题识别

    题解: ..感觉挺烦得 而且我都没有注意到树随机这件事情.. 就写个30分的莫队.. #include <bits/stdc++.h> using namespace std; #defi ...

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

    "Couple Trees" are two trees, a husband tree and a wife tree. They are named because they ...

  3. 2018湘潭邀请赛C题(主席树+二分)

    题目地址:https://www.icpc.camp/contests/6CP5W4knRaIRgU 比赛的时候知道这题是用主席树+二分,可是当时没有学主席树,就连有模板都不敢套,因为代码实在是太长了 ...

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

    解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序 ...

  5. Cutting Bamboos(2019年牛客多校第九场H题+二分+主席树)

    题目链接 传送门 题意 有\(n\)棵竹子,然后有\(q\)次操作,每次操作给你\(l,r,x,y\),表示对\([l,r]\)区间的竹子砍\(y\)次,每次砍伐的长度和相等(自己定砍伐的高度\(le ...

  6. SDOI2018:原题识别

    题解: https://files.cnblogs.com/files/clrs97/old-solution.pdf Code: #include<cstdio> #include< ...

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

    Boring Counting Time Limit: 3000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述     In this problem you a ...

  8. The 2019 Asia Nanchang First Round Online Programming Contest C(cf原题,线段树维护矩阵)

    题:https://nanti.jisuanke.com/t/41350 分析:先将字符串转置过来 状态转移,因为只有5个状态,所以 i 状态到 j 状态的最小代价就枚举[i][k]->[k][ ...

  9. BZOJ2223[Coci 2009]PATULJCI——主席树

    题目描述 输入  先输入一个数n,然后一个数表示这n个数中最大的是多少,接下来一行n个数.然后一个数m,最后m行询问每次两个数l,r. 输出 no或者yes+这个数 样例输入 10 3 1 2 1 2 ...

随机推荐

  1. C#抽象类与抽象方法--就是类里面定义了函数而函数里面什么都没有做的类

    看一下代码应该就可以了 using System; using System.Collections.Generic; using System.Linq; using System.Text; na ...

  2. ORACLE官网下载登陆账号能够使用

    username: responsecool@sina.com password: abc123ABC http://www.oracle.com/index.html

  3. 使用Highcharts生成折线图_at last

    //数据库数据的读取,读取数据后数据格式的转换,还有highchart数据源的配置,伤透了脑筋. anyway,最终开张了.哈哈! 数据库连接:conn_orcale.php <?php $db ...

  4. Spark笔记(一):错误总结

    1.转义字符: 常见的replaceAll,split,mkstring中涉及到特殊字符的都要加上转义字符,比如str.split("\\|"),str.replaceAll(&q ...

  5. 2017-2018-2 20155224『网络对抗技术』Exp5:MSF基础应用

    基础问题回答 用自己的话解释什么是exploit,payload,encode? exploit就相当于是载具,将真正要负责攻击的代码传送到靶机中,我觉得老师上课举的火箭和卫星的例子非常形象,火箭只是 ...

  6. MFC如何为程序添加标题

    1.在CMainFrame类中找到函数PreCreateWindow,在该函数中添加 cs.style &=~FWS_ADDTOTITLE;//去掉窗口的 自动标题 属性. 这句很重要不然的话 ...

  7. 【第四课】Linux的基础命令使用

    目录 一.passwd重置密码 二.单用户模式 三.救援模式 四.设置SElinux 五.Linux的常用基础命令详解 5.1.mkdir命令 5.2.ls命令 5.3.cd命令 5.4.chmod命 ...

  8. python3 简单进度条代码

    进度条代码函数实现 import sys, time class ShowProcess(object): """ 显示处理进度的类 调用该类相关函数即可实现处理进度的显 ...

  9. SSISDB4:当前正在运行的Package及其Executable

    SSISDB 系列随笔汇总: SSISDB1:使用SSISDB管理Package SSISDB2:SSIS工程的操作实例 SSISDB3:Package的执行实例 SSISDB4:当前正在运行的Pac ...

  10. Salesforce随笔: 解决被指定给Chatter相关用户的RecordType无法被删除的问题

    被指定给以下三组用户的RecordType无法在对应的Profile里取消占用: Chatter External User Chatter Free User Chatter Moderator U ...