[关闭]
@ysner 2018-07-19T15:39:41.000000Z 字数 1746 阅读 2055

打工

DP


题面

个人,可以分任意数量(不超过)的组,相同组的人用相同数字来表示。如果有多种表示,我们认为字典序最小的表示才是有效的。
询问某一表示是所有分组方式中的第几小。

解析

特别提醒:请将“表示”看作名词
看着这道题就想起了分组一题,两题分组的方式完全一样。

算法

每加入一个数,只有两种方案:新建组加入已有组
以此暴力枚举每一种方案,看该方案是第几个。

算法

这相当于问个人有多少种分组方式有木有。。。
预处理个人分为组(或者说这个人 所属组的编号 的最大值)的方案数
方程为


算法

我考场上一开始想到的是算法。
但这种算法不好回答这个表示是第几个。
试着比较两个表示,我们一般是怎样计算两者间差多少个表示来着?
就是不断往最后一位,如果超过表示中的最大值,就向前“进位”。
由此,我们可以看出,当一位增加时,后面(整个数)经历了多少种表达方式,而这本质是后面的数有多少种分组方式
如从,经历种。
我们求的也就是询问的表示经历了多少种。

于是我们就应该预处理这玩意儿。
状态不变,但应从后往前枚举。
边界
方程
然后就像进制数转十进制一般(都是数位上的数乘权值的和),从前往后,统计


就是询问表示经历的方案数。

这个状态就是用来与前面对接的,反映前面的状态,却存着后面的结果,所以有这个
不计是因为贡献反映在系数里。
当然可以滚掉一维。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #define re register
  8. #define il inline
  9. #define ll long long
  10. #define max(a,b) ((a)>(b)?(a):(b))
  11. #define min(a,b) ((a)<(b)?(a):(b))
  12. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  13. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  14. using namespace std;
  15. const int N=1e4+100,mod=1e6+7;
  16. int n,a[N],tong[N],tot,sum,ysn=1,dp[N][N],ans;
  17. il ll gi()
  18. {
  19. re ll x=0,t=1;
  20. re char ch=getchar();
  21. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  22. if(ch=='-') t=-1,ch=getchar();
  23. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  24. return x*t;
  25. }
  26. il void ok(re int &x){while(x>=mod) x-=mod;}
  27. int main()
  28. {
  29. n=gi();
  30. fp(i,1,n) a[i]=gi();
  31. fp(i,1,n-1) dp[n][i]=i+1;
  32. fq(i,n-1,1)
  33. fq(j,i,1)
  34. dp[i][j]=dp[i+1][j+1]+1ll*dp[i+1][j]*j%mod,ok(dp[i][j]);
  35. fp(i,2,n-1)
  36. {
  37. ans=ans+1ll*dp[i+1][ysn]*(a[i]-1)%mod,ok(ans);
  38. if(a[i]>ysn) ysn++;
  39. }
  40. ans=ans+a[n];ok(ans);
  41. printf("%d\n",ans);
  42. fclose(stdin);
  43. fclose(stdout);
  44. return 0;
  45. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注