ZF_20160625_Nod.LV5

来源:互联网 发布:windows安装ssh服务 编辑:程序博客网 时间:2024/05/19 20:40

1020 逆序排列
1-n逆序对数为k,求这种序列有多少个?
动态规划,加上一个前缀和。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int f[1001][20001],sum[2][20001],n=1000,k=20000;const int mod=1e9+7;int main(){    for(int i=1;i<=1000;i++)f[i][0]=1;    sum[1][0]=sum[0][0]=1;    for(int i=1;i<=n;i++)    {        int lim=min(i*(i-1)/2,k);        int cur=i&1,pre=1-cur;        for(int j=1;j<=lim;j++)        {            int L=j-i,R=j;            if(L<0)f[i][j]+=sum[pre][j];            else  f[i][j]+=(sum[pre][j]-sum[pre][j-i]);            f[i][j]=(f[i][j]%mod+mod)%mod;        }        for(int j=1;j<=i*(i+1)/2&&j<=k;j++)sum[cur][j]=(f[i][j]+sum[cur][j-1])%mod;    }    int cas;scanf("%d",&cas);    while(cas--)    {        int x,y;        scanf("%d %d",&x,&y);        printf("%d\n",f[x][y]);    }}

1040 最大公约数之和
Ans = Σ公约数*Euler(n/公约数)

#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;ll pnt,vis[40000],pri[6000],lim=32000;void tab(){    for(ll i=2;i<=lim;i++)    {        if(!vis[i])pri[++pnt]=i;        for(int j=i*2;j<=lim;j+=i)vis[j]=1;    }}namespace G{    ll fac[300],fnt;    ll get(ll p)    {        ll q=p;        for(ll i=1;i<=pnt;i++)        {            if(p%pri[i]==0)            {                q=q/pri[i]*(pri[i]-1);                while(p%pri[i]==0)p/=pri[i];            }            if(p==1)break;        }        if(p-1)q=q/p*(p-1);        return q;    }}int main(){    tab();    ll ans,n;    while(~scanf("%lld",&n))    {        ans=0;        for(ll i=1;i*i<=n;i++)        {            if(n%i==0)            {                if(i*i==n)ans+=i*G::get(i);                else ans+=i*G::get(n/i)+n/i*G::get(i);            }        }        printf("%lld\n",ans);    }}

1052 最大M子段和
f[i][j][2]
前i个数字,分成j段,1代表第i个数一定取,0代表不一定。

#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;ll n,m;ll f[2][5001][2],dat[5001];int main(){    while(~scanf("%lld%lld",&n,&m))    {        ll num=0,sum=0;        for(ll i=1;i<=n;i++)        {            scanf("%lld",&dat[i]);            if(dat[i]>0)num++,sum+=dat[i];        }        if(num<=m)        {            printf("%lld\n",sum);continue;        }        memset(f,0,sizeof f);        for(ll i=1;i<=n;i++)        {            int cur=i&1,pre=1-cur;            for(ll j=1;j<=m;j++)            {                f[cur][j][1]=max(max(f[pre][j][1]+dat[i],f[pre][j-1][1]+dat[i]),f[pre][j-1][0]+dat[i]);                f[cur][j][0]=max(f[pre][j][0],f[cur][j][1]);            }        }        printf("%lld\n",max(f[n&1][m][0],f[n&1][m][1]));    }}

1055 最长等差数列

(1)感觉题意说的不明白,其实数字次序可以重新排列的,因为这个浪费了很多时间。
(2)一定要用short int

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,dat[10001];short int f[10001][10001];int main(){    while(~scanf("%d",&n))    {        for(int i=1;i<=n;i++)scanf("%d",&dat[i]);        sort(dat+1,dat+n+1);        memset(f,0,sizeof f);        short int ans=0;        for(int j=2;j<=n-1;j++)        {            int i=j-1,k=j+1;            while(i>=1&&k<=n)            {                if(dat[i]+dat[k]>dat[j]*2)i--;                else if(dat[i]+dat[k]<dat[j]*2)k++;                else if(dat[i]+dat[k]==dat[j]*2)                {                    f[j][k]=f[i][j]==0?3:f[i][j]+1;                    ans=max(ans,f[j][k]);                    i--;k++;                }            }        }        printf("%d\n",ans);    }}

nod 1120
使用Lucas定理和卡特兰数
(1)h(n)=h(n-1)*(4*n-2)/(n+1)
(2)h(n)=C(2n,n)/(n+1)

ll Lucas(ll p,ll q){    ll ret=1,u,v;    while(p&&q)    {        u=p%mod;        v=q%mod;        ret=ret*C(u,v)%mod;        p/=mod;        q/=mod;    }    return ret;}

nod 1125
(1)考虑用最小重量的与其他的交换;
(2)开始我从最大重量的开始考虑,好像不对;
(3)交换次数一定小于n.

nod 1128
看到题目很容易想到二分结果,判断可不可以。

0 0
原创粉丝点击