【题解】毒瘤 OI 刷题汇总 [SCOI2014]

【题解】毒瘤 OI 刷题汇总 [SCOI2014]

由于不清楚题目顺序,就按照 \(\text{BZOJ}\) 上面的排列好了。


【Day1 T1】方伯伯的玉米田

传送门:方伯伯的玉米田 \(\text{[P3287]}\) \(\text{[Bzoj3594]}\)

【题目描述】

给出一个长为 \(n\) \((n\leqslant 10000)\) 的序列 \(a\) \((a_i\leqslant 5000)\),每次操作可以选择一个区间,使区间以内的所有数 \(+1\),最多可以进行 \(m\) \((m\leqslant 500)\) 次操作,求最后可以得到的最长单调不下降子序列长度。

【分析】

暴力 \(dp\) + 二维树状数组优化。

注意到一个很有用的性质:每次选择的区间右端点必定为 \(n\)(这不是很显然的吗)。由此可得:若 \(i\) 被操作了 \(k\) 次,则右边所有的数都至少被操作了 \(k\) 次。

\(dp[i][k]\) 表示 \(i\) 被操作了 \(k\) 次后,以 \(i\) 结尾的最长单调不下降子序列的最大长度,则有:

\(dp[i][k]=\max\{dp[j][l]+1\}\) \((0\leqslant j<i,\) \(0\leqslant l \leqslant k\) \(a_j+l\leqslant a_i+k)\)

发现这个东西可以用二维树状数组优化,第一维存 \(k\),第二维存 \(a_i+k\) 即可。由于存在下标为 \(0\) 的情况,所以要全部加 \(1\) 后再丢进树状数组,查询同理。

时间复杂度:\(O(mV\log m\log V)\),其中 \(V\)\(\max\{a_i+i\}\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=1e4+3,M=503; 
int n,m,ans,Max,A[N],dp[N][M];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct BIT{
    int n,m,C[M][5503];
    inline void CL(Re n_,Re m_){
        n=n_,m=m_;
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=m;++j)
                C[i][j]=-2e9;
    }
    inline void add(Re x,Re y,Re v){
        while(x<=n){
            Re j=y;
            while(j<=m)C[x][j]=max(C[x][j],v),j+=j&-j;
            x+=x&-x;
        }
    }
    inline int ask(Re x,Re y){
        Re ans=0;
        while(x){
            Re j=y;
            while(j)ans=max(ans,C[x][j]),j-=j&-j;
            x-=x&-x;
        }
        return ans;
    }
}TR;
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(m);
    for(Re i=1;i<=n;++i)in(A[i]),Max=max(Max,A[i]);
    TR.CL(m+1,Max+m+1),TR.add(0+1,0+1,0);
    for(Re i=1;i<=n;++i)
        for(Re k=m;k>=0;--k)
            ans=max(ans,dp[i][k]=max(dp[i][k],TR.ask(k+1,A[i]+k+1)+1)),TR.add(k+1,A[i]+k+1,dp[i][k]);
    printf("%d\n",ans);
}

【Day1 T2】方伯伯的 OJ

传送门:方伯伯的 \(\text{OJ}\) \(\text{[P3285]}\) \(\text{[Bzoj3595]}\)

【题目描述】

初始有 \(n\) \((1\leqslant n\leqslant 10^8)\) 个点,其权值分别为 \(1,2,3..n\),初始按照权值排名,给出 \(m\) \((1\leqslant m\leqslant 10^5)\) 个操作,操作种类如下(保证 \(x\) 合法,\(1\leqslant y\leqslant 2*10^8,\) \(1\leqslant k \leqslant n\)):

  • \(1\ x\ y:\) 将权值为 \(x\) 的点权值改为 \(y\),并输出它的排名

  • \(2\ x:\) 输出权值为 \(x\) 的点的排名,然后将它的排名提升到第一位

  • \(3\ x:\) 输出权值为 \(x\) 的点的排名,然后将它的排名降到最后一位

  • \(4\ k:\) 查询排名为 \(k\) 的点的权值

【分析】

毒瘤平衡树。

由于点数巨大,需要将用不到的点压缩起来,初始化只有一个权值区间为 \([1,n]\) 的点。

记录权值区间在 \(splay\) 上的编号可以用 \(map\),即对于每一个节点 \(p\) 所代表的区间 \([l,r]\),使 \(map[r]=p\),查询权值为 \(x\) 的点时直接在 \(map\) 上面 \(lower\_bound\) 即可。

当然这样子找到的可能是一段包含 \(x\) 的区间,所以要进行分裂,即新建两个节点分别存 \([l,x-1]\)\([x+1,r]\) 。做完这些事情之后就可以任意蹂躏包含 \(x\) 的这个节点了。

操作 \(2,3\) 可以分解为删点、加点两步操作,剩下的都是平衡树基操,没啥好说的。

当然也可以用线段树。

时间复杂度:\(O(m\log m)\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3;
int n,m,x,y,T,op,lastans;map<int,int>id;
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct Splay{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    int O,root;
    struct QAQ{int l,r,fa,size,ps[2];}tr[N<<2];
    inline void pushup(Re p){
        if(p)tr[p].size=tr[p].r-tr[p].l+1+tr[pl].size+tr[pr].size;
    }
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        connect(x,fa,fas),connect(fa,p,fas^1),connect(p,pa,pas);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p,Re to){
        for(Re fa;pf!=to;rotate(p))
            if(tr[fa=pf].fa!=to)rotate(which(p)==which(fa)?fa:p);
        if(!to)root=p;
    }
    inline int find(Re p,Re k){
        if(k<=tr[pl].size)return find(pl,k);
        else{
            Re tmp=tr[p].r-tr[p].l+1;k-=tr[pl].size;
            return k<=tmp?tr[p].l+k-1:find(pr,k-tmp);
        }
    }
    inline int split(Re p,Re v){
        if(tr[p].l<v){
            ++O,tr[O].l=tr[p].l,tr[O].r=v-1,id[v-1]=O;
            tr[p].l=v;
            connect(pl,O,0),connect(O,p,0);
            pushup(O),pushup(p);
        }
        if(v<tr[p].r){
            ++O,tr[O].l=v+1,tr[O].r=tr[p].r,id[tr[p].r]=O;
            tr[p].r=v,id[v]=p;
            connect(pr,O,1),connect(O,p,1);
            pushup(O),pushup(p);
        }
        return p;
    }
    inline int find_pre(Re p){p=pl;while(pr)p=pr;return p;}
    inline int find_nex(Re p){p=pr;while(pl)p=pl;return p;}
    inline int find_min(){Re p=root;while(pl)p=pl;return p;}
    inline int find_max(){Re p=root;while(pr)p=pr;return p;}
    inline void change(Re p,Re op){//移动到op端
        splay(p,0),printf("%d\n",lastans=tr[pl].size+1);
        if(!tr[p].ps[op])return;//不需要移动
        if(!tr[p].ps[op^1])swap(pl,pr);//在另一个极端,直接交换左右儿子
        else{
            splay(op?find_nex(p):find_pre(p),0),connect(op?pl:pr,root,op^1),pushup(root);
            pl=pr=pf=0,pushup(p);
            connect(p,op?find_max():find_min(),op);
            pushup(p),pushup(pf),splay(p,0);
        }
    }
    inline void sakura(Re p,Re v){//修改p的权值
        splay(id[v]=p,0),tr[p].l=tr[p].r=v,pushup(p);
        printf("%d\n",lastans=tr[pl].size+1);
    }
}T1;
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(T),T1.root=T1.O=id[n]=1,T1.tr[1].l=1,T1.tr[1].r=n,T1.pushup(1);
    while(T--){
        in(op),in(x),x-=lastans;
        if(op>3)printf("%d\n",lastans=T1.find(T1.root,x));
        else{
            Re p=(*id.lower_bound(x)).second;p=T1.split(p,x);//printf("id[x]=p=%d ",p);
            if(op<2)in(y),y-=lastans,id.erase(id.find(x)),T1.sakura(p,y);
            else T1.change(p,op-2);
        }
    }
}

【Day1 T3】方伯伯打扑克

传送门:方伯伯打扑克 \(\text{[P3284]}\) \(\text{[Bzoj3596]}\)

【题目描述】

略。

【分析】

死题,不会。

【Code】

不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)

【Day2 T1】方伯伯运椰子

传送门:方伯伯运椰子 \(\text{[P3288]}\) \(\text{[Bzoj3597]}\)

【题目描述】

略。

【分析】

一直搞不太懂分数规划,先咕着。

【Code】

不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)

【Day2 T2】方伯伯的商场之旅

传送门:方伯伯的商场之旅 \(\text{[P3286]}\) \(\text{[Bzoj3598]}\)

【题目描述】

略。

【分析】

又是毒瘤数位 \(dp\),不会,先咕着。

【Code】

不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)

【Day2 T3】舌尖上的方伯伯

传送门:舌尖上的方伯伯 \(\text{[P3289]}\) \(\text{[Bzoj3599]}\)

【题目描述】

略。

【分析】

\(SCOI\) 的毒瘤算几一向都极其不可做,先咕着。

【Code】

不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)

posted @ 2020-04-18 16:12  辰星凌  阅读(330)  评论(0编辑  收藏  举报