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