权值线段树+动态开点(学习小结)

首先先上一道板题:把它看懂就OK了,其实这个跟普通的线段树也没太大区别,就是存的是出现的次数。

CODE

#include<algorithm>
#include<cstdio> 
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
const int N=200050;
using namespace std;
int t[N*18],ls[N*18],rs[N*18],x,now,add,tot;
int tag[N*18];
int n,nowmin,rt;
char s[2];
void R(int &n)
{
    int t=0,p=1;char ch;for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';n=t*p;
}
void push(int p)
{
    if (tag[p])
    {
        tag[p]=0;
        ls[p]=(!ls[p])?++tot:ls[p];
        rs[p]=(!rs[p])?++tot:rs[p];
        t[ls[p]]=t[rs[p]]=0;
        tag[ls[p]]=tag[rs[p]]=1;
    }
}
void maintain(int p){t[p]=t[ls[p]]+t[rs[p]];}
void ins(int l,int r,int x,int &p)
{
    p=(!p)?++tot:p;
    if (l==r)
    {t[p]++; return;}
    push(p);int m=l+r>>1;
    if (x<=m) ins(l,m,x,ls[p]);
    else ins(m+1,r,x,rs[p]);
    maintain(p);
}
void update(int l,int r,int x,int y,int &p)
{
    p=(!p)?++tot:p;
    if (x<=l&&r<=y)
    {tag[p]=1;t[p]=0;return;}
    push(p);int m=l+r>>1;
    if (x<=m)update(l,m,x,y,ls[p]);
    if (m<y)update(m+1,r,x,y,rs[p]);
    maintain(p);
}
int query(int l,int r,int x,int &p)
{
    if (l==r) return l;
    push(p);int m=l+r>>1;
    if (x<=t[ls[p]]) return query(l,m,x,ls[p]);//树上二分,如果左区间的数的个数大于k,则往左区间走
    else return query(m+1,r,x-t[ls[p]],rs[p]);//否则减去左边数的个数
}
int main()
{
    //freopen("jzoj1558.in","r",stdin);
    //freopen("jzoj1558.out","w",stdout);
    R(n);R(nowmin);
    fo(i,1,n)
    {
        scanf("%s",s+1);R(x);
        if (s[1]=='I'){if (x-add<nowmin)continue;now++;ins(-N,N,x-add,rt);};
        if (s[1]=='A'){add+=x;nowmin-=x;};
        if (s[1]=='S'){add-=x;nowmin+=x;update(-N,N,-N,nowmin-1,rt);};
        if (s[1]=='F')
            if (x>t[1])puts("-1");
            else  printf("%d\n",query(-N,N,t[1]-x+1,rt)+add);
    }
    printf("%d",now-t[1]);
    return 0;
}

感觉挺裸的,但是由于我太弱了,去学了下别人的代码,才学会了。
然后上一道例题(自己领会的确是挺爽):

4270. 【NOIP2015模拟10.27】魔道研究

Description

“我希望能使用更多的魔法。不对,是预定能使用啦。最终我要被大家称呼为大魔法使。为此我决定不惜一切努力。”
——《The Grimoire of Marisa》雾雨魔理沙
魔理沙一如既往地去帕秋莉的大图书馆去借魔导书(Grimoire) 来学习魔道。
最开始的时候,魔理沙只是一本一本地进行研究。然而在符卡战中,魔理沙还是战不过帕秋莉。
好在魔理沙对自己的借还和研究结果进行了记录,从而发现了那些魔导书的精妙之处。
帕秋莉的那些魔导书,每本都有一个类别编号ti 和威力大小pi。而想要获得最有威力的魔法,就必须同时研究一些魔导书。而研究的这些魔导书就必须要满足,类别编号为T 的书的本数小于等于T,并且总共的本数小于等于一个给定的数N。而研究这些魔导书之后习得的魔法的威力就是被研究的魔导书的威力之和。
为了击败帕秋莉,魔理沙想要利用自己发现的规律来获得最有威力的魔法。
她列出了计划中之后M 次的借还事件,并想要知道每个事件之后自己所能获得的魔法的最大威力。可她忙于魔法材料——蘑菇的收集,于是这个问题就交给你来解决了。

Input

输入文件grimoire.in。
第1 行2 个整数N,M,分别表示魔理沙能研究的魔导书本数的上限和她的借还事件数。
之后M 行,每行的形式为“op t p”(不含引号)。Op 为“BORROW” 或“RETURN”,分别表示借书和还书。T 为一个整数,表示这本书的类别编号。P为一个整数,表示这本书的威力大小。注意,还书时如果有多本书满足类别编号为t,威力大小为p,这表明这些书都是相同的,魔理沙会任选其中一本书还回去。如果你问我为何会有相同的书,多半因为这是魔导书吧。

Output

输出文件grimoire.out。
一共M 行,每行一个整数,即每个事件之后的最大威力。

Sample Input

5 10
BORROW 1 5811
BORROW 3 5032
RETURN 3 5032
BORROW 3 5550
BORROW 5 3486
RETURN 1 5811
RETURN 3 5550
BORROW 4 5116
BORROW 3 9563
BORROW 5 94

Sample Output

5811
10843
5811
11361
14847
9036
3486
8602
18165
18259

Data Constraint

对于5% 的数据,1 <= t,N,M <= 50。
对于10% 的数据,1 <= t,N,M <= 100。
对于30% 的数据,1 <= t,N,M<= 10 000。
另有30% 的数据,1 <= p <= 1 000。
对于100% 的数据,1 <= t,N,M <= 300 000,1<= p<= 1 000 000 000。
另外,总共有30% 的数据,满足没有“RETURN” 操作。这部分数据均匀分布。

solution

转换一下问题模型,其实就是我们有很多棵小树,然后第i棵的前i个数,又可以放到一个大树中,然后我们要维护大树中前n大的数的和,那就很简单了,我们借一本书的时候,我们判断这本书的威力值是否大于对应的小树中的第i个,如果是,则删除在大树中的原来小树中的第i数,加入这个数到大树中,然后无论如何都是要将这个数加入到小树中的,删除类似。

code

#include<cstdio> 
#include<algorithm>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
const int M=1000000000;
const int N=300000+55;
typedef long long ll;
using namespace std;
int t[N*90],ls[N*90],rs[N*90],rt[N*90],tot,n,m;
char s[7];
ll c[N*90];
void R(int &n)
{
    int t=0,p=1;char ch;
    for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())
    if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) 
    t=t*10+ch-'0';n=t*p;
}
void ins(int l,int r,int x,int &p,int w)
{
    p=(!p)?++tot:p;
    if (l==r)
    {
        t[p]+=w;
        c[p]=1ll*l*t[p];
        return; 
    }
    int m=l+r>>1;
    if (x<=m) ins(l,m,x,ls[p],w);
    else ins(m+1,r,x,rs[p],w);
    t[p]=t[ls[p]]+t[rs[p]];
    c[p]=c[ls[p]]+c[rs[p]];
}
int query(int l,int r,int x,int &p)
{
    if (l==r) return l;
    int m=l+r>>1;
    if (x<=t[rs[p]]) return query(m+1,r,x,rs[p]);//注意跟上题不同,这里是第k大,上面是第k小。(注意上面本质是求第k大,只是我将它n-k+1了)
    else return query(l,m,x-t[rs[p]],ls[p]);
}
ll sum(int l,int r,int n,int &p)
{
    if (l==r) return 1ll*l*n;
    int m=l+r>>1;
    if (n<=t[rs[p]]) return sum(m+1,r,n,rs[p]);
    else return sum(l,m,n-t[rs[p]],ls[p])+c[rs[p]];
}
int main()
{
    freopen("grimoire.in","r",stdin);
    freopen("grimoire.out","w",stdout);
    int z,p;
    R(n);R(m);
    fo(i,1,m)
    {
        scanf("%s%",s+1);
        R(z);R(p);
        if (s[1]=='B')
        {
            if (t[rt[z]]<z)
            {
                ins(1,M,p,rt[z],1);
                ins(1,M,p,rt[0],1);
            }
            else
            {
                int k=query(1,M,z,rt[z]);
                if (p>k)
                {
                    ins(1,M,p,rt[z],1);
                    ins(1,M,p,rt[0],1);
                    ins(1,M,k,rt[0],-1);
                }
                else 
                    ins(1,M,p,rt[z],1);
            }
        }
        else
        {
            if (t[rt[z]]<=z)
            {
                ins(1,M,p,rt[z],-1);
                ins(1,M,p,rt[0],-1);
            }
            else
            {
                int k=query(1,M,z+1,rt[z]);
                if (p>=k)
                {
                    ins(1,M,p,rt[z],-1);
                    ins(1,M,p,rt[0],-1);
                    ins(1,M,k,rt[0],1);
                }
                else 
                    ins(1,M,p,rt[z],-1);
            }
        }
        if (t[rt[0]]<=n) printf("%lld\n",c[rt[0]]);
        else
        printf("%lld\n",sum(1,M,n,rt[0]));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ganjingxian/article/details/81101216