「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}(k\leq j\leq 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\n",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\n",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\n",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\n",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\n",ans[i]);
    }
}
View Code

思路积累

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

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

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-10-12 10:59  Wwb_star  阅读(146)  评论(0编辑  收藏  举报