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\n",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\n",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\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2017-10-26 20:53  lher  阅读(169)  评论(0编辑  收藏  举报