• 「10.11」chess(DP,组合数学)·array(单调栈)·ants(莫队,并茶几)


    菜鸡wwb因为想不出口胡题所以来写题解了

    A. chess


    昨天晚上考试,有点困

    开考先花五分钟扫了一边题,好开始肝$T1$

    看了一眼$m$的范围很大,第一反应矩阵快速幂??

    $n$很小,那么可以打$n^4$的DP,

    $10min$过去了,好像就是一个$DP$啊,随便乘个组合数就好了,

    最后距离考试$20min$时,因为瞎取模,把自己的$AC$覆盖了kukukuku

    正解的话,首先对于第一列而言,第$1+n$列的放的$C$的个数与他相同

    但是因为只知道数目我们乘上组合数就好

    $f_{ij}$表示第$i$行,一共放了$j$个棋子的方案数,转移即可

    思路积累:

    1。快速幂要预处理

    2。指数不能取模

    3。观察数据范围合理进行递推

    B. array


    觉得题还是不错的考场想了半个小时想到的

    题意:

    给出一个序列,每个数有一个权值,求满足$a_{k}leq a_{j}leq a_{i}(kleq jleq i)$中$k$的最小值

    我的做法是维护两个单调栈,一个单调递增,一个单调递减

    假设当前单减的栈的只栈顶是$maxtop$,递减的是$mintop$;

    对于单减的栈当我们插入$i$后,$st_{maxtop-1}$是左边第一个大于$i$的节点,

    所以我们已经满足了第一个条件,

    对于第二个条件因为在单增栈中对于任意节点$k$,从它到栈顶的所有值都大于它

    因此我们通过$st_{maxtop-1}$直接在单增栈中$upperbound$即可

    当然这是考场瞎打水过的,并不是正解。

    事实上不用维护第二个栈只需要在每次弹递增栈时记录一个数组$pos_{j}$表示从$j$开始到左侧第一比他大的元素中

    权值最小的元素位置,没一段$pos_{j}$都控制一段区域,可以在弹栈中维护。

    思路积累:

    1.对于区间具有单调性的问题可以考虑单调栈

    2.单减的栈保证栈中每个元素到栈首的值都小于等于该元素

    单增保证每个元素到栈首的值都大于等于该元素

    C. ants


    听说是原题,我又没做过......

    $50$算法

    莫队+线段树,维护区间最长连续区间和

    $100$算法

    回滚莫队+并查集

    第一次接触回滚莫队,莫队大法吼....

    简单叙述一下

    对于一些问题我们发现对于区间的移动而言加减的操作很难维护,那么为了比较好的时间复杂度,可以采用回滚莫队

    对于该题,他的删数操作我们很难维护区间最大子段和,那么我们开始思考我们令操作中只有加操作即可

    那么我们采用分块思想,区间左端点所在块为第一关键字,右端点为第二关键字

     bool cmp(no a,no b){return (bel[a.l]==bel[b.l])?(a.r<b.r):(a.l<b.l);} 

    然后我们选一个块,发现我们先处理出$L-r_{x}$的部分,

    也就是说处理出左端点到他所在的块的最右端,然后我们发现对于右端点是单调的

    那么我们保持右端点的贡献不清空,每次清空左段点到块的那部分

    当然假如两端点在同一块内就直接暴力处理

    至于并查集

    采用按秩合并思想,然后我们用一个栈记录以前的相连的点,相当与是时间戳一样

    然后每次回溯撤销

    注意对于右边区间我们要继承上一状态

    #include<bits/stdc++.h>
    #define int long long
    #define MAXN 1100000
    using namespace std;
    int read(){
        char c=getchar();int x=0;
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return x;
    }
    int deep[MAXN],fa[MAXN];int n,m;int a[MAXN];
    struct node{
        int fa,to;
        void insert(int x,int y){fa=x;to=y;}
    }T[MAXN];int top=0;
    void del(){
        while(top){
            fa[T[top].to]=T[top].to;
            deep[T[top].fa]-=deep[T[top].to];
            top--;
        }
    }
    int ans[MAXN];int bel[MAXN];int kuan;int l[MAXN],r[MAXN];
    struct no{int l,r,id;}e[MAXN];
    int find(int x){
        if(fa[x]==0)return 0;
        if(fa[x]==x)return x;
        return find(fa[x]);
    }
    bool cmp(no a,no b){return (bel[a.l]==bel[b.l])?(a.r<b.r):(a.l<b.l);}
    void init(){
        memset(l,0x3f3f3f,sizeof(l));
        kuan=sqrt(n);
        for(int i=1;i<=n;++i){
            int me=(i-1)/kuan+1;
            bel[i]=me;
            l[me]=min(i,l[me]);
            r[me]=max(i,r[me]);
        }
    } 
    vector<int>v[MAXN];
    int merge(int x,int y,int opt){
        if(deep[x]>=deep[y]){
            deep[x]+=deep[y];
            fa[y]=x;
            if(opt)T[++top].insert(x,y);
            return deep[x];
        }
        else{
            deep[y]+=deep[x];
            fa[x]=y;
            if(opt)T[++top].insert(y,x);
            return deep[y];
        }
    }
    void work(int x){
        int R=r[x];//printf("x=%lld
    ",x);
        int rs=0;
        for(int k=0;k<v[x].size();++k){
            int to=v[x][k];
            int ll=e[to].l;int rr=e[to].r;
            //printf("ll=%lld rr=%lld be=%lld ber=%lld
    ",ll,rr,bel[ll],bel[rr]);
            if(bel[ll]==bel[rr]){
                int maxn=0;
                for(int i=ll;i<=rr;++i){
                    fa[a[i]]=a[i];deep[a[i]]=1;maxn=max(deep[a[i]],maxn);
                    int fa1=find(a[i]-1);
                    if(fa1){maxn=max(merge(fa1,a[i],1),maxn);}
                    int fa2=find(a[i]+1);int me=find(a[i]);
                    if(fa2){maxn=max(merge(fa2,me,1),maxn);}
                }
                ans[e[to].id]=maxn;
                del();
                for(int i=ll;i<=rr;++i)fa[a[i]]=0,deep[a[i]]=0;
            }
            else{
                int maxn=0;
                for(int i=R+1;i<=rr;++i){
                    //printf("work2 a[%lld]=%lld
    ",i,a[i]);
                    fa[a[i]]=a[i];deep[a[i]]=1;maxn=max(deep[a[i]],maxn);
                    int fa1=find(a[i]-1);
                    if(fa1){maxn=max(merge(fa1,a[i],0),maxn);rs=max(maxn,rs);}
                    int fa2=find(a[i]+1);int me=find(a[i]);
                    if(fa2){maxn=max(merge(fa2,me,0),maxn);rs=max(maxn,rs);}                
                }            
                for(int i=ll;i<=r[x];++i){
                    //printf("work1 a[%lld]=%lld
    ",i,a[i]);
                    fa[a[i]]=a[i];deep[a[i]]=1;maxn=max(deep[a[i]],maxn);
                    int fa1=find(a[i]-1);
                    if(fa1){maxn=max(merge(fa1,a[i],1),maxn);}
                    int fa2=find(a[i]+1);int me=find(a[i]);
                    if(fa2){maxn=max(merge(fa2,me,1),maxn);}                
                }
                ans[e[to].id]=max(maxn,rs);
                del();
                R=rr;
                for(int i=ll;i<=r[x];++i)fa[a[i]]=0,deep[a[i]]=0;
            }
        }
        for(int i=r[x]+1;i<=R;++i)fa[a[i]]=0,deep[a[i]]=0;
    }
    signed main(){
        //freopen("1.in","r",stdin);
        //freopen("w.out","w",stdout);
        n=read();m=read();
        for(int i=1;i<=n;++i){
            a[i]=read();
        }
        init();
        for(int i=1;i<=m;++i){
            e[i].l=read();e[i].r=read();e[i].id=i;
        }
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=m;++i){int to=e[i].l;v[bel[to]].push_back(i);}
        for(int i=1;i<=(n-1)/kuan+1;++i){
            if(!v[i].size())continue;
            work(i);
        }
        for(int i=1;i<=m;++i){
            printf("%lld
    ",ans[i]);
        }
    }
    View Code

    思路积累

    1.回滚莫队处理比较难的区间操作

    2.查询区间连续子段长度,可以按秩合并并查集

  • 相关阅读:
    nyoj 202 红黑树
    nyoj 237 游戏高手的烦恼
    nyoj 203 三国志
    nyoj 118 修路方案
    nyoj 714 Card Trick
    nyoj 710 外星人的供给站
    nyoj 712探 寻 宝 藏
    nyoj 709 异 形 卵
    nyoj 711 最舒适的路线
    HTML5表格简单应用案例之[招聘需求表]
  • 原文地址:https://www.cnblogs.com/Wwb123/p/11660266.html
Copyright © 2020-2023  润新知