CSP-S 模拟76

  最简单的T1没去做,T2,T3倒是A了

  

 

 

   序列

  将序列分成B块,每块长度最大为A,每一个块都是连续的上升序列,且前一个块所有权值大于后边所有块的所有权值

  也就是类似与(8,9,10)(5,6,7)(2,3,4))(1)的样子,显然最长上升子序列不超过A,所有块中取出一个值组成最长下降子序列B

#include<iostream>
#include<cstdio>
using namespace std;
int T,n,A,B,a[110000];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&A,&B);
        if(n-A<B-1||A*B<n){
            puts("No");
            continue;
        }
        for(register int i=1,j=n-A+1;i<=A;i++,j++)    a[i]=j;
        if(B>1){
            int las=n-A+1;
            int bg=A;
            for(register int i=B-1;i>=1;i--){
                int w=min(las-i,A);
                bg+=w;
                for(register int j=1,k=bg;j<=w;j++,k--){
                    a[k]=--las;
                }
            }
        }
        puts("Yes");
        for(register int i=1;i<=n;i++) printf("%d ",a[i]);
        puts("");
    }
}
View Code

 

  购物

  显然$k\in[(sum+1)/2,sum] $,可以做一个背包(大神说的,我不知道什么叫背包QWQ),维护k的取值区间,加上一个a[i],就是将所有区间基础上加上[(a[i]+1)/2,a[i]](保留原来区间,加入新的区间),会发现会有一堆连起来的区间

  显然是个板子——Old Driver tree (老司机树)——又称柯朵莉树

#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
#include<vector>
using namespace std;
struct node{
    long long l,r;
    bool operator < (const node x)const{
        return (l<x.l)||(l==x.l&&r<x.r);
    }
};
set<node>s;
vector<node>v;
int n,a[110000];
void add(int x){
    auto it=s.begin();
    for(;it!=s.end();it++){
        node w=*it;
        v.push_back((node){w.l+(x+1)/2,w.r+x});
    }
    while(v.size()){
        s.insert(v.back());
        v.pop_back();
    }
}
void split(){
    auto it=s.begin();
    for(;it!=s.end();){
        auto L=it;
        it++;
        if(it==s.end()) break;
        if((*L).r>=(*it).l){
            node w=(node){(*L).l,(*it).r};
            s.erase(it);
            s.erase(L);
            s.insert(w);
            it=s.lower_bound(w);
        }
    }
}
int main(){
    //freopen("2.in","r",stdin);
    //freopen("2.out","w",stdout);
    scanf("%d",&n);
    for(register int i=1;i<=n;i++) scanf("%d",&a[i]);
    s.insert((node){0,0});
    for(register int i=1;i<=n;i++){
        add(a[i]);
        split();
    }
    long long ans=0;
    auto it=s.begin();
    for(;it!=s.end();it++){
        if((*it).l==0&&(*it).r==0) continue;
        ans+=((*it).r-(*it).l+1);
    }
    printf("%lld\n",ans);
}
View Code

 

  计数

  首先要知道: 前序遍历——根,左,右

           中序遍历——左,根,右

           后序遍历——左,右,根

  对于$M=0$即无限制的情况,我们只需要考虑前序遍历,对于一个前序遍历$[l,r]$,第一个位置$l$一定是根,从第二个位置开始到某一个位置是左子树,从该位置后一个位置到最后是右子树,可以枚举$i\in[l,r]$,$[l+1,i]$为左子树,$[i+1,r]$为右子树,那么方案数是$\sum\limits_{i=l}^{r}f[l+1][i]*f[i+1][r]$,包含无左子树和无右子树的情况

  那么就可以不断划分子问题,知道l==r或l>r,返回1即可

  但是这样会T成狗,可以发现对于任意长度为len的序列,在没有限制的情况下方案数是固定的,所以用数组f[len]记录长度为len的区间的方案数,记忆化搜索一下会快到飞起

  考虑加上限制,限制有两种: 在中序遍历中 $1.$$u$先于$v$出现 $2.$$u$晚于$v$出现

  因为先序遍历是从$1$到$n$,$u$,$v$之间有谁大谁小的不同情况

  我们让它一边倒,只考虑$v<u$的情况,因为先序遍历是从小的为根再考虑到大的,所以$v>u$的情况会在$u$的位置考虑到

  对于$v<u$的情况,在先序遍历所能构造的二叉树中,$u$要么在$v$的某一个祖宗的右子树中,要么在v的子树中——左子树或右子树

  • 对于中序遍历,$u$先于$v$出现,考虑上面的情况,$u$必定在$v$的左子树中,那么在划分$[v,r]$的序列时,$[v+1,u]$的区间只能是左子树,即枚举$v$的左右子树的分界线至少要在$u$及其以后才行
  • 对于中序遍历,$u$晚于$v$出现,同样考虑上面的情况,$u$只能在$v$的祖宗的右子树,或v的右子树中。
    • 对于第一种在祖宗的右子树中,在考虑祖宗的子树的时候,$v$,$u$不会被划分到一个序列里,因此在$v$为根的时候不需要考虑;
    • 对于第二种在$v$的右子树中,和$u$先与$v$出现类似,$u$必须是右子树,所以$[u,r]$只能是右子树,即枚举$v$的左右子树的分界线最大也要小于$u$

  综上所述,我们可以预处理出$L[x]$,$R[x]$,分别表示以$x$做根,枚举左右子树边界i的范围,然后dfs搜索即可,而对于记忆化,因为现在对于长度相等的序列在有限制的条件下方案不一样,但是对于一个区间$[l,r]$的方案数是肯定不变的,所以记忆化$f[l][r]$即可

  $L[x]$,为大于x的里边最后一个在中序遍历中先于$x$出现的点

  $R[x]$,为大于$x$的里边第一个在中序遍历中晚于$x$出现的点的前一个位置

  注意如果$R[x]$大于当前以$x$打头的序列$[l,r]$的$r$,那么说明$R[x]$那些晚于$x$出现的点在$x$的祖宗的右子树中,所以枚举分界线的右边界为$min(R[x],r)$

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,m,a[410][410],L[410],R[410];
const long long mod=1e9+7;
long long f[410],dp[410][410];
long long dfs(int l,int r){
    if(l>r) return f[0]=1;
    if(l==r) return f[1]=1;
    if(f[r-l+1]) return f[r-l+1];
    long long ans=0;
    for(register int i=l;i<=r;i++){
        f[r-l+1]=(f[r-l+1]+dfs(l+1,i)*dfs(i+1,r)%mod)%mod;
    }
    f[r-l+1]%=mod;
    return f[r-l+1];
}
long long DFS(int l,int r){
    if(l>r) return 1;
    if(L[l]>r) return 0;
    if(l==r) return 1;
    if(dp[l][r]!=-1) return dp[l][r];
    dp[l][r]=0;
    for(register int i=L[l];i<=min(R[l],r);i++){
        dp[l][r]=(dp[l][r]+DFS(l+1,i)*DFS(i+1,r)%mod)%mod;
    }
    return dp[l][r];
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        if(m==0) printf("%lld\n",dfs(1,n));
        else{
            memset(a,0,sizeof(a));
            memset(dp,-1,sizeof(dp));
            for(register int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),a[x][y]=1;
            for(register int i=1;i<=n;i++){
                L[i]=i,R[i]=i;
                for(register int j=i+1;j<=n;j++){
                    if(a[j][i])    L[i]=j;
                }
                for(register int j=i+1;j<=n;j++){
                    if(a[i][j])    break;
                    R[i]=j;
                }
            }
            printf("%lld\n",max(DFS(1,n),0ll));
        }
    }
}
View Code

 

  

posted @ 2019-10-17 09:01  Lockey_T  阅读(199)  评论(2编辑  收藏  举报