标签:can 线段 roo 防止 注意 第一个 坑点 技术 node
昨天突然脑袋比较清醒,好像似乎以前没有搞太懂的可持久化线段树一下子就搞懂了,结果打了几遍还是出现了一些意想不到的问题,下面我就来整理一下,防止以后重蹈覆辙!
下面我放一个50分的代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct sd{
LL l,r,cnt,son[2];
}node[1000005];
const LL Max=1e9+7;
LL ini[1000004],rot[1000004],n,m,cnt;
void update(LL k){node[k].cnt=node[node[k].son[0]].cnt+node[node[k].son[1]].cnt;}
/*void modify(LL k,LL l,LL r,LL pos)
{
node[k].l=l;node[k].r=r;LL mid=(l+r)/2;
if(l==r) node[k].cnt++;
else if(pos<=mid)
{
++cnt;
node[cnt]=node[node[k].son[0]];
node[k].son[0]=cnt;
modify(node[k].son[0],l,mid,pos);
}
else
{
++cnt;
node[cnt]=node[node[k].son[1]];
node[k].son[1]=cnt;
modify(node[k].son[1],mid+1,r,pos);
}
update(k);
}*/
LL query(LL lroot,LL rroot,LL rank)
{
if(node[rroot].l==node[rroot].r)
return node[rroot].l;//strange
else
{
LL delta=node[node[rroot].son[0]].cnt-node[node[lroot].son[0]].cnt;
if(rank<=delta) return query(node[lroot].son[0],node[rroot].son[0],rank);
else return query(node[lroot].son[1],node[rroot].son[1],rank-delta);
}
}
inline void modify(LL k,long long l,long long r,LL val)
{
node[k].l=l;node[k].r=r;
if(l==r)
{
node[k].cnt++;return;
}
long long mid=l+r;mid/=2;
cnt++;
node[cnt]=node[node[k].son[0]];
node[k].son[0]=cnt;cnt++;
node[cnt]=node[node[k].son[1]];
node[k].son[1]=cnt;
if(val<=mid)
{
modify(node[k].son[0],l,mid,val);
}
else
{
modify(node[k].son[1],mid+1,r,val);
}
update(k);
}
//LL query(LL rl,LL rr,LL val)
//{
// if(node[rr].l==node[rr].r) return node[rr].l;//very very important
// LL delta=node[node[rr].son[0]].cnt-node[node[rl].son[0]].cnt;
// if(val<=delta)
// return query(node[rl].son[0],node[rr].son[0],val);
// else
// return query(node[rl].son[1],node[rr].son[1],val-delta);
//}
int main()
{
scanf("%lld%lld",&n,&m);
for(LL i=1;i<=n;++i) scanf("%lld",&ini[i]);
//rot[0]=1;//important
for(LL i=0;i<=n;++i)
{
rot[i]=++cnt;node[rot[i]]=node[rot[i-1]];modify(rot[i],0,2*Max,Max+ini[i]);
}
LL a,b,c;
for(LL i=1;i<=m;++i)
{
scanf("%lld%lld%lld",&a,&b,&c);
printf("%lld\n",query(rot[a-1],rot[b],c)-Max);
}
return 0;
}
我们可以发现后面的几个点是出了问题的其实就是RE,这个原因其实很容易就可以发现,因为我们如果要查一号位置的话,然后我们就会惊讶的发现,我们会query零号根(rot[0])然后我们把0号根定义的是到1号节点,然后就出事了!!!
所以说这里cnt应该初始化到1,或者直接改为
rot[0]=++cnt;
然后我确实是发现了这个问题,但是交上去发现还是RE
这是为什么?
然后我发现这次RE的原因是数组开小了,至少电脑是这样认为的,但是其实我的数组是开的够大的,是因为我的动态开点modify的时候开点开了很多没有必要的点,所以导致了空间浪费(我本来以为这样开点可以使开出来的树更加的平衡)但是事实上证明我是错的,因为大家可以好好理解一下为什么下面的query中的第一个if语句一定写的是右根而不是左根!!!
这篇博客对于上面那个query中的小细节有所解释:
主席树(可持久化线段树)
然后把这里改了,在这里再注意下这个主席树节点个数要开到10000000(别数了,一千万)然后你就可以AC这道题了,坑点还是很多的!!!
AC code:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct sd{
LL l,r,cnt,son[2];
}node[10000005];
const LL Max=1e9+7;
LL ini[1000004],rot[1000004],n,m,cnt;
void update(LL k){node[k].cnt=node[node[k].son[0]].cnt+node[node[k].son[1]].cnt;}
void modify(LL k,LL l,LL r,LL pos)
{
node[k].l=l;node[k].r=r;LL mid=(l+r)/2;
if(l==r) node[k].cnt++;
else
{
if(pos<=mid)
{
++cnt;
node[cnt]=node[node[k].son[0]];
node[k].son[0]=cnt;
modify(node[k].son[0],l,mid,pos);
}
else
{
++cnt;
node[cnt]=node[node[k].son[1]];
node[k].son[1]=cnt;
modify(node[k].son[1],mid+1,r,pos);
}
update(k);
}
}
LL query(LL lroot,LL rroot,LL rank)
{
if(node[rroot].l==node[rroot].r)
return node[rroot].l;//strange
else
{
LL delta=node[node[rroot].son[0]].cnt-node[node[lroot].son[0]].cnt;
if(rank<=delta) return query(node[lroot].son[0],node[rroot].son[0],rank);
else return query(node[lroot].son[1],node[rroot].son[1],rank-delta);
}
}
int main()
{
scanf("%lld%lld",&n,&m);
for(LL i=1;i<=n;++i) scanf("%lld",&ini[i]);
rot[0]=++cnt;//important
for(LL i=1;i<=n;++i)
{
rot[i]=++cnt;node[rot[i]]=node[rot[i-1]];modify(rot[i],0,2*Max,Max+ini[i]);
}
LL a,b,c;
for(LL i=1;i<=m;++i)
{
scanf("%lld%lld%lld",&a,&b,&c);
printf("%lld\n",query(rot[a-1],rot[b],c)-Max);
}
return 0;
}
By njc
标签:can 线段 roo 防止 注意 第一个 坑点 技术 node
原文地址:https://www.cnblogs.com/mudrobot/p/13329097.html