zoukankan      html  css  js  c++  java
  • 20171026校内训练

    海棠数组啊,差分后线段树乱搞就过了

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int sum[500100],l[500100],r[500100],c[500100],a[500100];
    int Abs(int x){return x>0?x:-x;}
    void build(int L,int R,int x)
    {
        l[x]=L;r[x]=R;
        if(L==R){sum[x]=c[L];return;}
        build(L,(L+R)/2,x<<1);build((L+R)/2+1,R,x<<1|1);
        sum[x]=max(sum[x<<1],sum[x<<1|1]);
    }
    void add(int x,int kk,int k)
    {
        if(l[x]==r[x]&&l[x]==kk){sum[x]+=k;return;}
        int mid=(l[x]+r[x])/2;
        if(kk<=mid)add(x<<1,kk,k);
        else add(x<<1|1,kk,k);
        sum[x]=max(sum[x<<1],sum[x<<1|1]);
    }
    int query(int x,int L,int R)
    {
        if(l[x]==L&&r[x]==R){return sum[x];}
        int mid=(l[x]+r[x])/2;
        if(R<=mid)return query(x<<1,L,R);
        else if(L>mid)return query(x<<1|1,L,R);
        else return max(query(x<<1,L,mid),query(x<<1|1,mid+1,R));
    }
    int main()
    {
        freopen("lipschitz.in","r",stdin);freopen("lipschitz.out","w",stdout);
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            c[i-1]=Abs(a[i]-a[i-1]);
        }
        build(1,n-1,1);
        int q;scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            int type,l,r;scanf("%d%d%d",&type,&l,&r);
            if(type)
            {
                if(r<=l)puts("0");
                else printf("%d
    ",query(1,l,r-1));  
            }
            else
            {
                a[l]=r;int k1=Abs(a[l]-a[l-1]),k2=Abs(a[l+1]-a[l]);
                if(l!=1){add(1,l-1,k1-c[l-1]);c[l-1]=k1;}
                if(l!=n){add(1,l,k2-c[l]);c[l]=k2;}
            }
        }
        return 0;
    }
    View Code

    正解:

    正难则反
    定义坏的位置i满足:ai-1<ai>ai+1
    f[i][j]表示前i个数有j个不好的,所以f[1][1]=1;

    f[i][j] 有2种情况:
    1.这个位置是坏的:
    那么 它不可以填所有坏的的两边=>f[i][j]=f[i-1][j-1]*(i-(j-1)*2)
    2.这个位置是好的
    那么 它可以填在所有坏的的两边=>f[i][j]=f[i-1][j]*j*2
    综上可得:
    f[i][j]=f[i-1][j-1]*(i-(j-1)*2)+f[i-1][j]*j*2.

    我的蜜汁做法:

    我们用f[i-1][n-j+1]表示前i个数有j个好位置的方案数,至于为什么要这样,等等再说

    先看看下表,第i行第j列表示前i+1个数有j个好位置的方案数

    2
    2 4
    0 16 8
    0 16 88   16
    0 0   272 416    32
    0 0   272 2880  1824       64
    0 0   0     7936  24576     7680         128
    0 0   0     7936  137216   185856     31616       256
    0 0   0     0        353792   1841152   1304832   128512   512
    0 0   0     0        353792   9061376   21253376 8728576 518656 1024

    但我们发现这表并不太好看,于是我们右对齐

                                                                                                                   2
                                                                                                        2         4
                                                                                        0             16        8
                                                                     0                16           88        16
                                                       0            0                272        416        32
                                       0             0             272          2880       1824       64
                            0          0             0           7936        24576      7680     128
                    0       0        0       7936         137216      185856     31616     256
              0      0     0        0       353792     1841152    1304832   128512   512
       0     0     0     0   353792   9061376   21253376   8728576  518656   1024

    我们随便算几个数,发现

    272=0*8+272*1

    7936=272*8+2880*2

    137216=7936*8+24576*3

    2880=272*6+416*3

    24576=2880*6+1824*4

    185856=24576*6+7680*5

    1824=416*4+32*5

    7680=1824*4+64*6

    31616=7680*4+128*7

    我们发现了什么?f[i][j]=f[i-1][j]*(从右往左数是第几列*2)+f[i-1][j+1]*(它自己和自己上方不是0的个数)

    即f[i][j]=f[i-1][j]*(n-j+1)*2+f[i-1][j+1]*(i-(n-j)-(n-j-1))(从右往左数第j列的上方有j-2个0)

    这样,我们就可以O(n^2)求出这个数组,然后统计答案时,随便for一下就好了

    注意,对于每个询问,当k>=n时输出0,注意上表第一行是n=2的情况,且上表没有k=0的情况

    当k=0时,则答案为n!(因为你可以随便排列)

    建议f数组和ans开long long,否则计算过程中有可能溢出,还要记得取膜,输出ans时要用lld

    打表:

    #include<cstdio>
    #include<cstring>
    using namespace std;
    bool used[100];
    int a[100],ans[100],n;
    void dfs(int now){
        if(now==n+1){
            int tmp=0;
            for(int i=1;i<=n;i++)if(a[i-1]>a[i]||a[i+1]>a[i])tmp++;
            ans[tmp]++;return;
        }
        for(int i=1;i<=n;i++)if(!used[i])used[i]=true,a[now]=i,dfs(now+1),used[i]=false;
    }
    int main(){
        freopen("xxx.out","w",stdout);
        for(n=2;n<=20;n++){
            memset(ans,0,sizeof(ans));dfs(1);
            for(int k=1;k<=n-1;k++){
                printf("%10d",ans[k]);
            }puts("");
        }
    }
    View Code

    计算:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int mod=1e9+7;
    long long f[3010][3010];
    int main()
    {
        freopen("permutation.in","r",stdin);
        freopen("permutation.out","w",stdout);
        int n=3000;
        f[1][n]=2;
        for(int i=2;i<=n;i++)
        for(int j=n-i+1;j<=n;j++)
        f[i][j]=(f[i-1][j]*(long long)(n-j+1)*2+f[i-1][j+1]*(long long)(i-(n-j)-(n-j-1)))%mod;
        int q;scanf("%d",&q);
        while(q--)
        {
            int n1,k1;long long ans=0;scanf("%d%d",&n1,&k1);
            if(k1==0){ans=1;for(int i=1;i<=n1;i++)ans=(ans*i)%mod;printf("%lld
    ",ans);continue;}
            if(k1>=n1){puts("0");continue;}
            for(int i=n-n1+k1+1;i<=n;i++)ans=(ans+f[n1-1][i])%mod;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    [leetcode] Delete Operation for Two Strings
    [leetcode] Minimum ASCII Delete Sum for Two Strings
    [leetcode] Palindromic Substrings
    [leetcode] Student Attendance Record I
    [leetcode] Reverse String II
    [leetcode] Diameter of Binary Tree
    [leetcode] Climbing Stairs
    [leetcode] Range Sum Query
    Codeforces 1294A Collecting Coins
    团体程序设计天梯赛 L2-021 点赞狂魔 (25分)
  • 原文地址:https://www.cnblogs.com/lher/p/7739383.html
Copyright © 2011-2022 走看看