zoukankan      html  css  js  c++  java
  • [ZROI 9.16模拟赛] Tutorial

    Link:

    传送门

    A:

    套路题结果想了好久……

    排序二叉树的性质就是中序遍历单调递增

    于是只考虑当前树的中序遍历的序列即可,与树的形态无关

    将序列改成严格单调增想到最大化不变的数,但直接LIS求的是改为非严格单调增的数

    一个将严格单调增问题改为非严格的套路是将数$a_i$替换成$a_i-i$,对转换后序列求LIS即可

    (其实也可以理解为在严格单增问题中能拓展的条件为$a[i]-a[k]ge i-k$,那么也就是$a[i]-ige a[k]-k$)

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=2e5+10;
    ll dat[MAXN],ind[MAXN],tot;
    int n,x,y,ch[MAXN][2],dp[MAXN],res;
    
    void dfs(int x)
    {
        if(~ch[x][0]) dfs(ch[x][0]);
        ind[++tot]=dat[x];
        if(~ch[x][1]) dfs(ch[x][1]);
    }
    
    int main()
    {
        scanf("%d",&n);
        memset(ch,-1,sizeof(ch));
        for(int i=1;i<=n;i++) 
            scanf("%lld",&dat[i]);
        for(int i=1;i<n;i++)
            scanf("%d%d",&x,&y),ch[x][y]=i+1;
        dfs(1);dp[0]=-1<<30;
        
        for(int i=1;i<=n;i++) ind[i]-=i;
        for(int i=1;i<=n;i++)
        {
            if(ind[i]>=dp[res]) dp[++res]=ind[i];
            else dp[upper_bound(dp+1,dp+res+1,ind[i])-dp]=ind[i];
        }
        printf("%d",n-res);
        return 0;
    }
    Problem A

    B:

    长度可行性单调,对长度二分答案

    发现区间$[l,r]$中存在$k$的条件为:$gcd(l...r)=min(l...r)=k$

    区间最小和$gcd$明显可以用$RMQ$维护,但此题卡$log^2$,因此只能用$ST$表来维护

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=5e5+10;
    int res[MAXN],tot;
    int n,dat[MAXN],lg2[MAXN],mn[MAXN][50],gcd[MAXN][50];
    int GCD(int x,int y){return x%y==0?y:GCD(y,x%y);}
    
    void pre()
    {
        lg2[1]=0;
        for(int i=2;i<=n;i++)
            lg2[i]=lg2[i-1]+((1<<(lg2[i-1]+1))==i);
        
        for(int i=n;i>=1;i--)
        {
            mn[i][0]=dat[i];
            for(int j=1;(i+(1<<j)-1)<=n;j++)
                mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
        }
        for(int i=n;i>=1;i--)
        {
            gcd[i][0]=dat[i];
            for(int j=1;(i+(1<<j)-1)<=n;j++)
                gcd[i][j]=GCD(gcd[i][j-1],gcd[i+(1<<j-1)][j-1]);
        }
    }
    int Query_min(int l,int r)
    {
        int t=lg2[r-l+1];
        return min(mn[l][t],mn[r-(1<<t)+1][t]);
    }
    int Query_gcd(int l,int r)
    {
        int t=lg2[r-l+1];
        return GCD(gcd[l][t],gcd[r-(1<<t)+1][t]);
    }
    
    bool check(int len)
    {
        tot=0;
        for(int i=1;i<=n-len+1;i++)
            if(Query_min(i,i+len-1)==Query_gcd(i,i+len-1))
                res[++tot]=i;
        return tot>0;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) 
            scanf("%d",&dat[i]);
        pre();
        
        int l=1,r=n;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)) l=mid+1;
            else r=mid-1;
        }
        check(r);
        printf("%d %d
    ",tot,r-1);
        for(int i=1;i<=tot;i++) printf("%d ",res[i]);
        return 0;
    }
    Problem B

    如果只有询问用$ST$表$O(1)$询问

    同时注意由于对一个数多次求$gcd$不会影响区间$gcd$值,因此可以直接用$ST$表维护$gcd$

    C:

    很像以前冒泡排序相关题的一个$Trick$:

    由于交换序列是一个排列,因此每次交换后左右不可能再有交换,这样就拆为独立的子问题了

    由于初始状态的值有规律是单调的,因此反向考虑问题:

    对于当前区间$[l,r]$枚举交换位置$i$,如果交换$i,i+1$后左序列是$[l,l+i-1]$的一个排列则计算其贡献

    $dp[l][r]=dp[l][l+i-1]*dp[l+i][r]*C^{i-1}_{r-l+1-2}$

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=105,MOD=1e9+7;
    int n,dat[MAXN];
    ll C[MAXN][MAXN],dp[MAXN][MAXN];
    
    ll dfs(int l,int r)
    {
        if(~dp[l][r]) return dp[l][r];
        if(l==r) return dp[l][r]=1;
        //不能开全局…… 
        int st[MAXN],top=0;dp[l][r]=0;
        for(int i=1;i<=n;i++)
            if(dat[i]>=l&&dat[i]<=r)
                st[++top]=dat[i];
        
        for(int i=1;i<top;i++)
        {
            swap(st[i],st[i+1]);
            bool f=1;
            for(int j=1;j<=i;j++)
                if(st[j]>=l+i){f=0;break;}
            for(int j=i+1;j<=top;j++)
                if(st[j]<l+i){f=0;break;}
            if(!f){swap(st[i],st[i+1]);continue;}
            
            (dp[l][r]+=dfs(l,l+i-1)*dfs(l+i,r)%MOD*C[r-l-1][i-1]%MOD)%=MOD;
            swap(st[i],st[i+1]);
        }
        return dp[l][r];
    }
    
    int main()
    {
        scanf("%d",&n);
        memset(dp,-1,sizeof(dp));
        for(int i=1;i<=n;i++)
            scanf("%d",&dat[i]);
        for(int i=0;i<=n;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=i;j++)
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
        }
        dfs(0,n-1);
        
        if(~dp[0][n-1]) 
            printf("%lld",dp[0][n-1]);
        else puts("0");
        return 0;
    }
    Problem C

    注意这里每次处理的$[l,r]$的排列是$p$的子序列!

    (处理到该子问题时能保证数在$p$中的相对位置不变)

    D:

    关键在于贡献为$2^{R+C}$,可以理解为对每一个子集算一次贡献

    接下来算每个集合被包含的期望次数即可:

    $res=sum C^i_n*C^j_n*frac{C^{k-num}_{m-num}}{C^k_m}$

    其中$num$为如果$i$行$j$列全涂黑的个数,预处理组合数即可

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=1e5+10;
    int n,m,k;
    db res,cn[MAXN],cm[MAXN];
    
    double cal(int i,int j)
    {
        int num=n*(i+j)-i*j;
        return k-num<0?0:cm[num];
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        cn[0]=cm[0]=1;
        for(int i=1;i<=n;i++) cn[i]=cn[i-1]*(n-i+1)/i;
        for(int i=1;i<=m;i++) cm[i]=cm[i-1]*(k-i+1)/(m-i+1);
        
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                res+=cn[i]*cn[j]*cal(i,j);
        printf("%.6lf",(res>1e99)?1e99:res);
        return 0;
    }
    Problem D
  • 相关阅读:
    <DC guide ---6>
    <DC guide ---5>
    <DC guide ---3>
    <DC guide ---4>
    linux下的EDA——VCS使用
    xilinx小实验——chipscope指导
    VGA控制的verilog模块设计
    verilog时钟分频设计(整合模块)
    verilog时钟分频设计
    testbench——双向端口的仿真
  • 原文地址:https://www.cnblogs.com/newera/p/9669268.html
Copyright © 2011-2022 走看看