【UR #20】跳蚤电话

将加边变为加点,方案数为 \((n-1)!\) 除以一个数,\(dp\) 每种方案要除的数之和即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. int ver[200005],ne[200005],head[100005],cnt;
  5. inline void link(int x,int y){
  6. ver[++cnt]=y;
  7. ne[cnt]=head[x];
  8. head[x]=cnt;
  9. }
  10. const int md=998244353;
  11. int dp[100005],siz[100005],inv[100005];
  12. void dfs(int x,int fi){
  13. int res=1;siz[x]=1;
  14. for(int i=head[x];i;i=ne[i]){
  15. int u=ver[i];
  16. if(u==fi)continue;
  17. dfs(u,x);siz[x]+=siz[u];
  18. res=1ll*res*dp[u]%md;
  19. }
  20. for(int i=head[x];i;i=ne[i]){
  21. int u=ver[i];
  22. if(u==fi)continue;
  23. dp[x]=(dp[x]+1ll*res*siz[u]%md*inv[siz[x]]%md*inv[siz[x]-siz[u]])%md;
  24. }
  25. dp[x]=(dp[x]+1ll*res*inv[siz[x]])%md;
  26. }
  27. int main(){
  28. scanf("%d",&n);
  29. for(int i=1;i<n;i++){
  30. int x,y;scanf("%d%d",&x,&y);
  31. link(x,y);link(y,x);
  32. }
  33. inv[0]=inv[1]=1;
  34. for(int i=2;i<=n;i++)inv[i]=1ll*(md-md/i)*inv[md%i]%md;
  35. int ans=1;
  36. for(int i=head[1];i;i=ne[i]){
  37. int u=ver[i];
  38. dfs(u,1);ans=1ll*ans*dp[u]%md;
  39. }
  40. for(int i=1;i<n;i++)ans=1ll*ans*i%md;
  41. printf("%d\n",ans);
  42. return 0;
  43. }

【UR #12】密码锁

发现一张竞赛图的强连通分量个数等于将这张图划分成两个集合使得集合之间的边方向相同的方案数 +1 ,因此可以枚举集合的划分,这里直接做的复杂度是 \(O(2^n)\) ,发现对于一个联通块射击到的点数最多为边数 +1 ,因此可以对特殊边所涉及到的每个联通块分别枚举划分,做一下背包即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int md=998244353,inv2=(md+1)/2;
  4. inline int pwr(int x,int y){
  5. int res=1;
  6. while(y){
  7. if(y&1)res=1ll*res*x%md;
  8. x=1ll*x*x%md;y>>=1;
  9. }
  10. return res;
  11. }
  12. int n,m;
  13. int x,y,z;
  14. int aw[2005],bw[2005],cw[2005];
  15. vector<int> vec[45];
  16. int a[45][45],stk[45],top;
  17. bool vis[45];
  18. void init(int x){
  19. if(vis[x])return;
  20. vis[x]=1;
  21. stk[++top]=x;
  22. for(auto u:vec[x])init(u);
  23. }
  24. int f[45],g[45],h[45];
  25. int stk0[45],top0,stk1[45],top1;
  26. void dfs(int pos){
  27. if(pos>top){
  28. int res=1;
  29. for(int i=1;i<=top0;i++){
  30. for(int j=1;j<=top1;j++)res=1ll*res*a[stk0[i]][stk1[j]]%md;
  31. }
  32. res=1ll*res*bw[top0*top1]%md;
  33. g[top0]=(g[top0]+res)%md;
  34. return;
  35. }
  36. stk0[++top0]=stk[pos];
  37. dfs(pos+1);--top0;
  38. stk1[++top1]=stk[pos];
  39. dfs(pos+1);--top1;
  40. }
  41. inline void calc(){
  42. for(int i=0;i<=n;i++)h[i]=f[i],f[i]=0;
  43. for(int i=0;i<=n;i++){
  44. for(int j=0;i+j<=n;j++)f[i+j]=(f[i+j]+1ll*h[i]*g[j])%md;
  45. }
  46. }
  47. long long sum=0;
  48. int main(){
  49. scanf("%d%d",&n,&m);
  50. for(int i=1;i<=n;i++){
  51. for(int j=1;j<=n;j++)a[i][j]=5000;
  52. }
  53. aw[0]=1;for(int i=1;i<=n*n;i++)aw[i]=1ll*aw[i-1]*5000%md;
  54. for(int i=0;i<=n*n;i++)bw[i]=pwr(aw[i],md-2);
  55. cw[0]=1;for(int i=1;i<=n*n;i++)cw[i]=1ll*cw[i-1]*10000%md;
  56. for(int i=1;i<=m;i++){
  57. scanf("%d%d%d",&x,&y,&z);
  58. a[x][y]=z;a[y][x]=10000-z;
  59. vec[x].push_back(y);vec[y].push_back(x);
  60. }
  61. f[0]=1;
  62. for(int i=1;i<=n;i++)if(!vis[i]){
  63. top=0;init(i);
  64. for(int j=0;j<=n;j++)g[j]=0;
  65. dfs(1);calc();
  66. }
  67. int ans=0;
  68. for(int i=1;i<n;i++)ans=(ans+1ll*f[i]*aw[i*(n-i)]%md*cw[n*(n-1)/2-i*(n-i)])%md;
  69. ans=1ll*(ans+cw[n*(n-1)/2])%md*cw[n*(n-1)/2]%md;
  70. printf("%d\n",(ans+md)%md);
  71. return 0;
  72. }

【UR #17】滑稽树上滑稽果

显然,无论在什么情况下,最优解都是一条链,而且每个点的滑稽度不小于所有点的 \(\text{and}\) 之和,因此可以设 \(dp[s]\) 表示拉出一条链,使得链的最下面一个点的滑稽度为 \(s\) 的最小代价,暴力枚举转移是 \(O(n\times a)\) 的,实际上转移时只需要枚举子集,判断是否有点的权值与 \(S\) 与 \(T\) 的差集无交即可,时间复杂度 \(O(3^{\log a})\)

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. bool vis[1<<18];
  5. const int lim=(1<<18)-1;
  6. inline void fwt(){
  7. for(int i=0;i<18;i++){
  8. for(int s=0;s<(1<<18);s++){
  9. if((s>>i)&1)continue;
  10. vis[s|(1<<i)]|=vis[s];
  11. }
  12. }
  13. }
  14. long long dp[1<<18],tmp=lim;
  15. int main(){
  16. scanf("%d",&n);
  17. for(int i=1;i<=n;i++){
  18. int x;scanf("%d",&x);
  19. vis[x]=1;tmp&=x;
  20. }
  21. fwt();
  22. memset(dp,0x3f,sizeof(dp));
  23. dp[lim]=n*tmp;
  24. for(int s=lim;s;s--){
  25. if((s&tmp)!=tmp)continue;
  26. for(int u=(s&(s-1));1;u=(s&(u-1))){
  27. int delta=((s-u)^lim);
  28. if(vis[delta])dp[u]=min(dp[u],dp[s]+u-tmp);
  29. if(!u)break;
  30. }
  31. }
  32. printf("%lld\n",dp[tmp]);
  33. return 0;
  34. }

【UNR #2】梦中的题面

CF1342F Make It Ascending

CF1239E Turtle

可以发现最优摆放方式一定是最小值和次小值一个放左上角一个放右下角,上面升序排列,下面倒序排列。最优行走路线要么将上面一行走完,要么将下面一行走完。

背包算出将最小值和次小值去除后的所有可能,取最优结果即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. int a[55],sum;
  5. bitset<1250005> dp[25];
  6. int main(){
  7. scanf("%d",&n);
  8. for(int i=1;i<=n;i++)scanf("%d",&a[i]);
  9. for(int i=1;i<=n;i++)scanf("%d",&a[i+n]);
  10. sort(a+1,a+2*n+1);
  11. dp[0][0]=1;
  12. for(int i=3;i<=2*n;i++){
  13. sum+=a[i];
  14. for(int j=n-1;j;j--)dp[j]|=(dp[j-1]<<a[i]);
  15. }
  16. int ans=1e9,up=0;
  17. for(int i=0;i<=sum;i++)if(dp[n-1][i]){
  18. if(ans>max(a[1]+a[2]+i,a[1]+a[2]+sum-i)){
  19. ans=max(a[1]+a[2]+i,a[1]+a[2]+sum-i);
  20. up=i;
  21. }
  22. }
  23. vector<int> UP,DOWN;
  24. int tot=n-1;
  25. for(int i=3;i<=2*n;i++){
  26. if(tot&&dp[tot-1][up-a[i]]){
  27. UP.push_back(a[i]);
  28. up-=a[i];tot--;
  29. }else DOWN.push_back(a[i]);
  30. }
  31. sort(UP.begin(),UP.end());reverse(UP.begin(),UP.end());
  32. sort(DOWN.begin(),DOWN.end());
  33. printf("%d ",a[1]);
  34. for(auto it:DOWN)printf("%d ",it);puts("");
  35. for(auto it:UP)printf("%d ",it);
  36. printf("%d",a[2]);
  37. return 0;
  38. }

CF1403C Chess Rush

CF613E Puzzle Lover

将路径拆成三个部分,两边哈希,中间 \(dp\) ,需要特判长度为 \(1\) 和 \(2\) 的部分。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,m;
  4. char a[2][2005],s[2005];
  5. unsigned long long le[2][2005],ri[2][2005],hshs[2005],bas[2005];
  6. inline unsigned long long Get(unsigned long long *hsh,int l,int r){
  7. if(l>r)return 0;
  8. return hsh[r]-hsh[l-1]*bas[r-l+1];
  9. }
  10. inline unsigned long long rGet(unsigned long long *hsh,int l,int r){
  11. if(l>r)return 0;
  12. return hsh[l]-hsh[r+1]*bas[r-l+1];
  13. }
  14. inline void init(){
  15. bas[0]=1;
  16. for(int i=1;i<=max(n,m);i++)bas[i]=131*bas[i-1];
  17. for(int i=1;i<=m;i++)hshs[i]=hshs[i-1]*131+s[i];
  18. for(int i=1;i<=n;i++)le[0][i]=le[0][i-1]*131+a[0][i];
  19. for(int i=1;i<=n;i++)le[1][i]=le[1][i-1]*131+a[1][i];
  20. for(int i=n;i>=1;i--)ri[0][i]=ri[0][i+1]*131+a[0][i];
  21. for(int i=n;i>=1;i--)ri[1][i]=ri[1][i+1]*131+a[1][i];
  22. }
  23. const int md=1e9+7;
  24. int dp[2][2005][2005][2];
  25. inline int calc(){
  26. int res=0;init();
  27. for(int i=0;i<=n;i++){
  28. dp[0][i][0][0]=1;
  29. for(int j=1;j<=m;j++)dp[0][i][j][0]=0;
  30. for(int j=1;j<=m;j++)dp[0][i][j][1]=0;
  31. dp[1][i][0][0]=1;
  32. for(int j=1;j<=m;j++)dp[1][i][j][0]=0;
  33. for(int j=1;j<=m;j++)dp[1][i][j][1]=0;
  34. }
  35. for(int i=1;i<=n;i++){
  36. for(int j=2;j<=i&&2*j<=m;j++){
  37. if(rGet(ri[0],i-j+1,i)*bas[j]+Get(le[1],i-j+1,i)==Get(hshs,1,2*j))dp[1][i][2*j][1]=1;
  38. if(rGet(ri[1],i-j+1,i)*bas[j]+Get(le[0],i-j+1,i)==Get(hshs,1,2*j))dp[0][i][2*j][1]=1;
  39. }
  40. }
  41. for(int i=1;i<=n;i++){
  42. for(int j=1;j<=m;j++){
  43. if(a[0][i]==s[j]){
  44. dp[0][i][j][0]=(dp[0][i][j][0]+dp[0][i-1][j-1][0])%md;
  45. dp[0][i][j][0]=(dp[0][i][j][0]+dp[0][i-1][j-1][1])%md;
  46. if(j!=1)dp[0][i][j][1]=(dp[0][i][j][1]+dp[1][i][j-1][0])%md;
  47. }
  48. if(a[1][i]==s[j]){
  49. dp[1][i][j][0]=(dp[1][i][j][0]+dp[1][i-1][j-1][0])%md;
  50. dp[1][i][j][0]=(dp[1][i][j][0]+dp[1][i-1][j-1][1])%md;
  51. if(j!=1)dp[1][i][j][1]=(dp[1][i][j][1]+dp[0][i][j-1][0])%md;
  52. }
  53. // cout<<i<<" "<<j<<": "<<endl;
  54. // cout<<dp[0][i][j][0]<<" "<<dp[0][i][j][1]<<endl;
  55. // cout<<dp[1][i][j][0]<<" "<<dp[1][i][j][1]<<endl;
  56. if(((m-j)&1)||(m-j)/2>n-i||m-j==2)continue;
  57. if(Get(le[0],i+1,i+(m-j)/2)*bas[(m-j)/2]+rGet(ri[1],i+1,i+(m-j)/2)==Get(hshs,j+1,m)){
  58. // cout<<"calced 0"<<endl;
  59. res=(res+dp[0][i][j][0])%md;
  60. res=(res+dp[0][i][j][1])%md;
  61. }
  62. if(Get(le[1],i+1,i+(m-j)/2)*bas[(m-j)/2]+rGet(ri[0],i+1,i+(m-j)/2)==Get(hshs,j+1,m)){
  63. // cout<<"calced 1"<<endl;
  64. res=(res+dp[1][i][j][0])%md;
  65. res=(res+dp[1][i][j][1])%md;
  66. }
  67. }
  68. }
  69. // puts("---");
  70. return res;
  71. }
  72. int main(){
  73. scanf("%s",a[0]+1);n=strlen(a[0]+1);
  74. scanf("%s",a[1]+1);
  75. scanf("%s",s+1);m=strlen(s+1);
  76. int ans=0;
  77. ans=calc();
  78. reverse(a[0]+1,a[0]+n+1);
  79. reverse(a[1]+1,a[1]+n+1);
  80. ans=(ans+calc())%md;
  81. if(m==1){
  82. for(int i=1;i<=n;i++)if(a[0][i]==s[1])ans=(ans-1+md)%md;
  83. for(int i=1;i<=n;i++)if(a[1][i]==s[1])ans=(ans-1+md)%md;
  84. }
  85. if(m==2){
  86. for(int i=1;i<=n;i++)if(a[0][i]==s[1]&&a[1][i]==s[2])ans=(ans-1+md)%md;
  87. for(int i=1;i<=n;i++)if(a[1][i]==s[1]&&a[0][i]==s[2])ans=(ans-1+md)%md;
  88. }
  89. printf("%d",ans);
  90. return 0;
  91. }

CF1466H Finding satisfactory solutions

分析一下图的性质:

图中有若干个白色环(已确定)。然后连黑色边,使得所有的白色环形成一个 DAG ,并且一个点向 \(j\) 个点连黑色边的方案数为 \(j!(n-1-j)!\) 。

显然相同长度的白色环可以看做同一种。

设大小为 \(i\) 的白色环有 \(c_i\) 个,则不同的子图有 \(\prod(c_i+1)\) 个,这个数字不会超过 \(1440\) ,所以状态数很少。

然后考虑数 \(\text{DAG}\) 的个数,设 \(f(S)\) 为子图 \(S\) 构成 \(\text{DAG}\) 的方案数。

根据套路,枚举 \(S\) 的一个子图 \(T\) ,表示图中入度为 \(0\) 的点。将 \(T\) 连向 \(S-T\) ,此时还要满足 \(S-T\) 中的每一个点都要被连到,所以要容斥一下。

设 \(\mathrm{ways}(x,y)\) 表示 \(y\) 个点向 \(x\) 个点连边的方案数。

那么有转移 :

\[f_{S} = \sum_{T\subset S} f_{S-T} \times \mathrm{ways}(|S|-|T|,|T|)\times (-1)^{|T|+1}
\]
\[\mathrm{ways}(x,y) = (\mathrm{ways}(x,1))^y = (\sum_{i=0}^x \binom{x}{i} i!(n-i-1)!)^y
ways(x,y)=(ways(x,1))
\]

\(\mathrm{ways}\) 可以直接 \(O(n^2)\) 预处理,由于最多只有 \(1440\) 个状态,\(\text{DP}\) 可以直接枚举子图转移,复杂度 \(O(1440^2)\)

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. int a[45];
  5. const int md=1e9+7;
  6. bool vis[45];
  7. int Getsiz(int x,int siz){
  8. if(vis[x])return siz;
  9. vis[x]=1;return Getsiz(a[x],siz+1);
  10. }
  11. int c[45];
  12. int ways[45][45],fac[45],inv[45];
  13. inline int C(int x,int y){
  14. if(x<y)return 0;
  15. return 1ll*fac[x]*inv[y]%md*inv[x-y]%md;
  16. }
  17. inline void init(){
  18. fac[0]=fac[1]=inv[0]=inv[1]=1;
  19. for(int i=2;i<=n;i++)fac[i]=1ll*fac[i-1]*i%md;
  20. for(int i=2;i<=n;i++)inv[i]=1ll*(md-md/i)*inv[md%i]%md;
  21. for(int i=2;i<=n;i++)inv[i]=1ll*inv[i]*inv[i-1]%md;
  22. ways[0][0]=1;
  23. for(int i=0;i<=n;i++){
  24. for(int j=0;j<=i;j++)ways[1][i]=(ways[1][i]+1ll*C(i,j)*fac[j]%md*fac[n-1-j])%md;
  25. for(int j=2;j<=n;j++)ways[j][i]=1ll*ways[j-1][i]*ways[1][i]%md;
  26. }
  27. }
  28. int dp[2005],pre[45],nxt[45];
  29. inline int rk(int *tmp){
  30. int res=0;
  31. for(int i=1;i<=n;i++)res=(c[i]+1)*res+tmp[i];
  32. return res;
  33. }
  34. inline void translate(int *tmp,int x){
  35. for(int i=n;i;i--){
  36. tmp[i]=x%(c[i]+1);
  37. x/=(c[i]+1);
  38. }
  39. }
  40. int main(){
  41. scanf("%d",&n);init();
  42. for(int i=1;i<=n;i++)scanf("%d",&a[i]);
  43. for(int i=1;i<=n;i++)if(!vis[i])c[Getsiz(i,0)]++;
  44. dp[0]=1;
  45. int lim=1;
  46. for(int i=1;i<=n;i++)lim*=(c[i]+1);lim--;
  47. for(int i=1;i<=lim;i++){
  48. translate(nxt,i);
  49. int tot0=0,tot1=0;
  50. for(int j=1;j<=n;j++)tot0+=nxt[j],tot1+=nxt[j]*j;
  51. for(int j=0;j<i;j++){
  52. translate(pre,j);
  53. int tmp0=0,tmp1=0,coe=1;
  54. for(int j=1;j<=n;j++)tmp0+=pre[j],tmp1+=pre[j]*j;
  55. for(int j=1;j<=n;j++)coe=1ll*coe*C(nxt[j],pre[j]);
  56. if(!((tot0-tmp0)&1))coe*=-1;
  57. dp[i]=(dp[i]+1ll*dp[j]*coe%md*ways[tot1-tmp1][tmp1])%md;
  58. }
  59. }
  60. printf("%d\n",(dp[lim]+md)%md);
  61. return 0;
  62. }

CF582D Number of Binominal Coefficients

令 \(v_p(n)\)(\(p\) 是质数,\(n\) 是正整数)表示 \(n\) 的标准分解式中的 \(p\) 的幂次。

发现 \(v_p(n!)=\sum_{i=1}^{\infty}\limits\lfloor \frac{n}{p^i}\rfloor\) ,这等于将 \(n\) 写成 \(p\) 进制数之后的所有前缀之和。

而 \(v_p({n\choose k})=v_p(n!)-v_p(k!)-v_p((n-k)!)\) ,因此 \(v_p({n\choose k})\) 就等于 \(k\) 与 \(n-k\) 在 \(p\) 进制意义下相加时的进位的次数。

数位 \(dp\) 即可。

点击查看代码
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int md=1e9+7;
  4. int p,alpha,a[2005],len;
  5. char A[2005];
  6. struct Big_based_p{
  7. int len,x[4005];
  8. inline void init(){
  9. while(::len){
  10. long long t=0;
  11. for(int i=::len;i>=1;i--){
  12. t=t*10+a[i],a[i]=t/p,t%=p;
  13. if(!a[i]&&i==::len)--::len;
  14. }
  15. x[++len]=t;
  16. }
  17. }
  18. int f[3503][3503][2][2];
  19. inline void work(){
  20. f[len+1][0][0][1]=1;
  21. for(int i=len;i>=1;i--){
  22. int c1=1ll*(p+1)*p/2%md,c2=1ll*(x[i]+1)*x[i]/2%md,c3=1ll*p*(p-1)/2%md;
  23. int c4=1ll*x[i]*((p<<1)-x[i]-1)/2%md,c5=1ll*x[i]*(x[i]-1)/2%md,c6=1ll*x[i]*((p<<1)-x[i]+1)/2%md;
  24. for(int j=0;j<=len-i+1;j++){
  25. int f0=f[i+1][j][0][0],f1=f[i+1][j][0][1];
  26. int f2=f[i+1][j][1][0],f3=f[i+1][j][1][1];
  27. if(!f0&&!f1&&!f2&&!f3)continue;
  28. f[i][j][0][0]=(1ll*c1*f0%md+1ll*c2*f1%md+1ll*c3*f2%md+1ll*c4*f3%md)%md;
  29. f[i][j][0][1]=(1ll*(x[i]+1)*f1%md+1ll*(p-x[i]-1)*f3%md)%md;
  30. f[i][j+1][1][0]=(1ll*c3*f0%md+1ll*c5*f1%md+1ll*c1*f2%md+1ll*c6*f3%md)%md;
  31. f[i][j+1][1][1]=(1ll*x[i]*f1%md+1ll*(p-x[i])*f3%md)%md;
  32. }
  33. }
  34. int res=0;
  35. for(int i=alpha;i<=len;i++)res=(res+(f[1][i][0][0]+f[1][i][0][1])%md)%md;
  36. printf("%d\n",res);
  37. }
  38. }lim;
  39. int main(){
  40. scanf("%d%d%s",&p,&alpha,A+1);len=strlen(A+1);
  41. if(alpha>4000)return puts("0"),0;
  42. for(int i=1;i<=len;i++)a[i]=A[len-i+1]-'0';
  43. lim.init();lim.work();
  44. return 0;
  45. }

[AGC034E] Complete Compress

考虑枚举最终聚集的点,即枚举根节点,将不同子树中的棋子的深度两两抵消,判断是否能将所有棋子移到根。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. int a[2005];
  5. int ver[4005],ne[4005],head[2005],cnt;
  6. inline void link(int x,int y){
  7. ver[++cnt]=y;
  8. ne[cnt]=head[x];
  9. head[x]=cnt;
  10. }
  11. int siz[2005],dis[2005],f[2005];
  12. void dfs(int x,int fi){
  13. siz[x]=a[x];int son=0;
  14. for(int i=head[x];i;i=ne[i]){
  15. int u=ver[i];
  16. if(u==fi)continue;
  17. dfs(u,x);siz[x]+=siz[u];
  18. dis[x]+=dis[u]+siz[u];dis[u]=dis[u]+siz[u];
  19. if(dis[u]>dis[son])son=u;
  20. }
  21. if(!son)f[x]=0;
  22. else if(2*dis[son]<=dis[x])f[x]=dis[x]/2;
  23. else f[x]=dis[x]-dis[son]+min(f[son],(2*dis[son]-dis[x])/2);
  24. }
  25. int main(){
  26. scanf("%d",&n);
  27. for(int i=1;i<=n;i++)scanf("%1d",&a[i]);
  28. for(int i=1;i<n;i++){
  29. int x,y;scanf("%d%d",&x,&y);
  30. link(x,y);link(y,x);
  31. }
  32. int res=1e9;
  33. for(int i=1;i<=n;i++){
  34. for(int j=1;j<=n;j++)siz[j]=0;
  35. for(int j=1;j<=n;j++)dis[j]=0;
  36. for(int j=1;j<=n;j++)f[j]=0;
  37. dfs(i,i);
  38. if(dis[i]&1)continue;
  39. if(2*f[i]>=dis[i])res=min(res,dis[i]/2);
  40. }
  41. if(res==1e9)res=-1;
  42. printf("%d\n",res);
  43. return 0;
  44. }

[AGC020E] Encoding Subsets

枚举最后一段是否压缩,如何压缩,状态数不高于平方级别,可以记忆化搜索。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. const int md=998244353;
  5. map<__int128,int> dp[105];
  6. int dfs(__int128 x,int len){
  7. if(dp[len].count(x))return dp[len][x];
  8. int res=dfs(x>>1,len-1)*(x&1?2:1)%md;
  9. int len0=1;
  10. for(__int128 lim=2;len0<=len;lim<<=1,len0++){
  11. __int128 tmp=x%lim;
  12. int len1=len0;
  13. for(__int128 j=lim;len1+len0<=len;j*=lim,len1+=len0){
  14. tmp&=(x/j);
  15. res=(res+1ll*dfs(x/j/lim,len-len0-len1)*dfs(tmp,len0))%md;
  16. }
  17. }
  18. return dp[len][x]=res;
  19. }
  20. char s[105];
  21. int main(){
  22. scanf("%s",s);
  23. n=strlen(s);__int128 tmp=0;dp[0][0]=dp[1][0]=1;dp[1][1]=2;
  24. for(int i=0;i<n;i++)tmp=tmp*2+s[i]-'0';
  25. printf("%d\n",dfs(tmp,n));
  26. return 0;
  27. }

[AGC036D] Negative Cycle

图中不存在负环等价于差分约束系统有解,由于初始的 \(n-1\) 条边无法删去,因此最终的权值一定是单调不升的,差分后变为一个长度为 \(n-1\) 的 \(01\) 序列。

对于 \(i\to j\ (i<j)\) 的边,当 \(i,j\) 之间没有 \(1\) 时需要被删去,\(i\to j\ (i>j)\) 的边,在 \(i,j\) 之间有至少两个 \(1\) 时需要被删去,\(dp\) 即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. long long A[505][505],B[505][505],f[505][505],ans=1e18;
  5. inline long long val(int k,int j,int i){
  6. return B[j+1][i]+A[n][j]-A[n][k]-A[i][j]+A[i][k];
  7. }
  8. int main(){
  9. scanf("%d",&n);
  10. for(int i=1;i<=n;i++){
  11. for(int j=1;j<=n;j++){
  12. if(i==j)continue;
  13. scanf("%lld",&A[i][j]);
  14. }
  15. }
  16. for(int j=1;j<=n;j++){
  17. for(int i=j;i;i--)B[i][j]=B[i+1][j]+B[i][j-1]-B[i+1][j-1]+A[i][j];
  18. }
  19. for(int i=1;i<=n;i++){
  20. for(int j=1;j<=n;j++)A[i][j]=A[i][j]+A[i-1][j]+A[i][j-1]-A[i-1][j-1];
  21. }
  22. for(int i=1;i<n;i++){
  23. f[i][0]=val(0,0,i);ans=min(ans,f[i][0]+val(0,i,n));
  24. for(int j=1;j<i;j++){
  25. f[i][j]=1e18;
  26. for(int k=0;k<j;k++)f[i][j]=min(f[i][j],f[j][k]+val(k,j,i));
  27. ans=min(ans,f[i][j]+val(j,i,n));
  28. }
  29. }
  30. printf("%lld\n",ans);
  31. return 0;
  32. }

[AGC028D] Chords

发现这个圆上的问题其实可以直接铺平到序列上,考虑在每个联通块的最左和最右端点处统计该连通块。

预处理 \(g[i]\) 表示 \(i\) 给点两两之间任意匹配的方案数,对于每个区间,枚举最左边的小联通块,容斥掉不合法方案数即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,k;
  4. const long long md=1e9+7;
  5. int vis[605],tot[605][605];
  6. unsigned long long hsh[605][605],tag[605];
  7. inline unsigned long long rd(){
  8. return 1ll*rand()*rand()*20050115+rand()*2005+rand();
  9. }
  10. long long dp[605][605],g[605],ans;
  11. int main(){
  12. scanf("%d%d",&n,&k);n<<=1;
  13. g[0]=1;
  14. for(int i=2;i<=n;i+=2)g[i]=g[i-2]*(i-1)%md;
  15. for(int i=1;i<=k;i++){
  16. int x,y;scanf("%d%d",&x,&y);
  17. unsigned long long tmp=rd();
  18. tag[x]^=tmp;tag[y]^=tmp;vis[x]=-1;vis[y]=-1;
  19. }
  20. for(int i=1;i<=n;i++){
  21. for(int j=i;j<=n;j++){
  22. hsh[i][j]=hsh[i][j-1]^tag[j];
  23. tot[i][j]=tot[i][j-1]+1+vis[j];
  24. }
  25. }
  26. for(int len=1;len<=n;len++){
  27. for(int l=1;l+len<=n;l++){
  28. int r=l+len;
  29. if(hsh[l][r])continue;
  30. dp[l][r]=g[tot[l][r]];
  31. for(int t=l;t<r;t++)dp[l][r]=(dp[l][r]-dp[l][t]*g[tot[t+1][r]])%md;
  32. ans=(ans+dp[l][r]*g[tot[1][n]-tot[l][r]])%md;
  33. }
  34. }
  35. printf("%lld",(ans+md)%md);
  36. return 0;
  37. }

[AGC022F] Checkers

首先考虑连边,建出一棵树,把每次消掉的 \(B\) 的父亲设成 \(A\) ,那么就会发现每一个点的贡献就是 \(a_ic_ix_i\) 其中 \(a_i=2^d\),\(d\) 为深度,\(c_i\) 是 \(1\) 或 \(-1\)。

因为 \(x_i\) 很大,所以任意两个点之间的贡献可以看做不会互相抵消,那么也就是说对于两个方案只要存在一个 \(i\) 使得 \(c_i\) 或者 \(a_i\) 不同,那么就算做不同的方案。

然后发现 \(a_i\) 只跟深度有关,那么这里先讨论 \(c_i\),我们可以默认根节点的 \(c_i=1\),那么发现对于一个点,它的 \(c_i\) 取决于它儿子个数的奇偶性和父亲选这个儿子的时候,之前选了儿子个数的奇偶性。

然后我们考虑根据这个性质进行dp,设 \(f_{i,j}\) 表示选了 \(i\) 个点,有 \(j\) 个点的儿子个数是奇数

那么每次就可以枚举一个当前层数有 \(k\) 个节点,然后我们发现是偶数的儿子个数为 \(\frac {k-j}{2}\) (如果发现 \(k-j\) 的奇偶性不对那就不转移),也就是说如果不计算下一层的影响的话,将会有 \(\frac {k-j}{2}\) 个点与父亲的符号相同。

考虑计算下一层的影响,枚举一个 \(x\) 表示实际上有 \(x\) 个节点和父亲不同,那么至少需要 \(|\frac {k-j}{2}-x|\) 个奇数点。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int md=1e9+7;
  4. int n,dp[55][55];
  5. long long fac[55],inv[55];
  6. inline void init(){
  7. fac[0]=fac[1]=inv[0]=inv[1]=1;
  8. for(int i=2;i<=n;i++)fac[i]=fac[i-1]*i%md;
  9. for(int i=2;i<=n;i++)inv[i]=(md-md/i)*inv[md%i]%md;
  10. for(int i=2;i<=n;i++)inv[i]=inv[i]*inv[i-1]%md;
  11. }
  12. long long C(int n,int m){
  13. return fac[n]*inv[m]%md*inv[n-m]%md;
  14. }
  15. int main(){
  16. scanf("%d",&n);init();
  17. dp[1][1]=dp[1][0]=n;
  18. for(int i=1;i<n;i++){
  19. for(int j=1;j<=n-i;j++){
  20. for(int k=0;k<=j;k++){
  21. if((j-k)&1)continue;
  22. int x=(j-k)/2;
  23. for(int d=0;d<=j;d++){
  24. dp[i+j][abs(x-d)]=(dp[i+j][abs(x-d)]+dp[i][k]*C(n-i,j)%md*C(j,d))%md;
  25. }
  26. }
  27. }
  28. }
  29. printf("%d\n",dp[n][0]);
  30. return 0;
  31. }

CF379G New Year Cactus

仿照树上背包的形式,在仙人掌上做背包即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,m;
  4. int dp[3][2505][2505],tmp[2505],siz[2505],tot[2][3][2505],ANS[3][2505];
  5. inline void calc(int x,vector<int> vec){
  6. if(vec.size()==1){
  7. for(int i=0;i<=siz[x]+siz[vec[0]];i++)tmp[i]=-1e9;
  8. for(int i=0;i<=siz[x];i++){
  9. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[0][x][i]+dp[0][vec[0]][j]);
  10. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[0][x][i]+dp[1][vec[0]][j]);
  11. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[0][x][i]+dp[2][vec[0]][j]);
  12. }
  13. for(int i=0;i<=siz[x]+siz[vec[0]];i++)dp[0][x][i]=tmp[i];
  14. for(int i=0;i<=siz[x]+siz[vec[0]];i++)tmp[i]=-1e9;
  15. for(int i=0;i<=siz[x];i++){
  16. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[1][x][i]+dp[0][vec[0]][j]);
  17. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[1][x][i]+dp[1][vec[0]][j]);
  18. }
  19. for(int i=0;i<=siz[x]+siz[vec[0]];i++)dp[1][x][i]=tmp[i];
  20. for(int i=0;i<=siz[x]+siz[vec[0]];i++)tmp[i]=-1e9;
  21. for(int i=0;i<=siz[x];i++){
  22. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[2][x][i]+dp[0][vec[0]][j]);
  23. for(int j=0;j<=siz[vec[0]];j++)tmp[i+j]=max(tmp[i+j],dp[2][x][i]+dp[2][vec[0]][j]);
  24. }
  25. for(int i=0;i<=siz[x]+siz[vec[0]];i++)dp[2][x][i]=tmp[i];siz[x]+=siz[vec[0]];
  26. }
  27. else {
  28. int u=vec.back();vec.pop_back();
  29. reverse(vec.begin(),vec.end());vec.push_back(x);
  30. {
  31. int sum=siz[u];
  32. for(auto it:vec)sum+=siz[it];
  33. for(int i=0;i<=sum;i++)ANS[0][i]=-1e9;
  34. for(int i=0;i<=sum;i++)ANS[1][i]=-1e9;
  35. for(int i=0;i<=sum;i++)ANS[2][i]=-1e9;
  36. }
  37. for(int t=0;t<3;t++){
  38. int *f[3]={tot[0][0],tot[0][1],tot[0][2]},*g[3]={tot[1][0],tot[1][1],tot[1][2]};
  39. int sum=siz[u];for(int i=0;i<=sum;i++)f[t][i]=dp[t][u][i];
  40. for(int i=0;i<=sum;i++)f[(t+1)%3][i]=-1e9;
  41. for(int i=0;i<=sum;i++)f[(t+2)%3][i]=-1e9;
  42. for(auto it:vec){
  43. for(int i=0;i<=sum+siz[it];i++)tmp[i]=-1e9;
  44. for(int i=0;i<=sum;i++){
  45. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[0][i]+dp[0][it][j]);
  46. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[1][i]+dp[0][it][j]);
  47. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[2][i]+dp[0][it][j]);
  48. }
  49. for(int i=0;i<=sum+siz[it];i++)g[0][i]=tmp[i];
  50. for(int i=0;i<=sum+siz[it];i++)tmp[i]=-1e9;
  51. for(int i=0;i<=sum;i++){
  52. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[0][i]+dp[1][it][j]);
  53. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[1][i]+dp[1][it][j]);
  54. }
  55. for(int i=0;i<=sum+siz[it];i++)g[1][i]=tmp[i];
  56. for(int i=0;i<=sum+siz[it];i++)tmp[i]=-1e9;
  57. for(int i=0;i<=sum;i++){
  58. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[0][i]+dp[2][it][j]);
  59. for(int j=0;j<=siz[it];j++)tmp[i+j]=max(tmp[i+j],f[2][i]+dp[2][it][j]);
  60. }
  61. for(int i=0;i<=sum+siz[it];i++)g[2][i]=tmp[i];sum+=siz[it];
  62. swap(f[0],g[0]);swap(f[1],g[1]);swap(f[2],g[2]);
  63. }
  64. for(int i=0;i<=sum;i++)ANS[0][i]=max(ANS[0][i],f[0][i]);
  65. if(t!=2)for(int i=0;i<=sum;i++)ANS[1][i]=max(ANS[1][i],f[1][i]);
  66. if(t!=1)for(int i=0;i<=sum;i++)ANS[2][i]=max(ANS[2][i],f[2][i]);
  67. }
  68. int sum=siz[u];for(auto it:vec)sum+=siz[it];siz[x]=sum;
  69. for(int i=0;i<=sum;i++)dp[0][x][i]=ANS[0][i];
  70. for(int i=0;i<=sum;i++)dp[1][x][i]=ANS[1][i];
  71. for(int i=0;i<=sum;i++)dp[2][x][i]=ANS[2][i];
  72. }
  73. }
  74. vector<int> vec[2505];
  75. int dfn[2505],low[2505],cnt;
  76. int stk[2505],top;
  77. void tarjan(int x){
  78. siz[x]=1;dp[0][x][1]=-1e9;
  79. dp[1][x][0]=1;dp[1][x][1]=-1e9;dp[2][x][0]=-1e9;
  80. dfn[x]=low[x]=++cnt;stk[++top]=x;
  81. for(auto u:vec[x]){
  82. if(!dfn[u]){
  83. tarjan(u);low[x]=min(low[x],low[u]);
  84. if(low[u]>=dfn[x]){
  85. vector<int> tmp;
  86. while(stk[top]!=u)tmp.push_back(stk[top--]);
  87. tmp.push_back(stk[top--]);calc(x,tmp);
  88. }
  89. }
  90. else low[x]=min(low[x],dfn[u]);
  91. }
  92. }
  93. int main(){
  94. scanf("%d%d",&n,&m);
  95. for(int i=1;i<=m;i++){
  96. int x,y;scanf("%d%d",&x,&y);
  97. vec[x].push_back(y);vec[y].push_back(x);
  98. }
  99. tarjan(1);
  100. for(int i=0;i<=n;i++)printf("%d ",max(dp[0][1][i],max(dp[1][1][i],dp[2][1][i])));
  101. return 0;
  102. }

[USACO22FEB] Phone Numbers P

考虑将后三位的值以及某一位是否可以作为转移点记录在状态中,复杂度比较高,发现很多状态是没用的,写一个结构体,用 \(\text{map}\) 记录状态即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int T;
  4. int n,cnt[1<<10];
  5. char s[100005];
  6. const int md=1e9+7;
  7. bool vis2[1<<10],vis4[1<<10];
  8. inline void init(){
  9. vis2[(1<<1)+(1<<2)]=vis2[(1<<2)+(1<<3)]=vis2[(1<<4)+(1<<5)]=vis2[(1<<5)+(1<<6)]=vis2[(1<<7)+(1<<8)]=vis2[(1<<8)+(1<<9)]=1;
  10. vis2[(1<<1)+(1<<4)]=vis2[(1<<4)+(1<<7)]=vis2[(1<<2)+(1<<5)]=vis2[(1<<5)+(1<<8)]=vis2[(1<<3)+(1<<6)]=vis2[(1<<6)+(1<<9)]=1;
  11. vis4[(1<<1)+(1<<2)+(1<<4)+(1<<5)]=vis4[(1<<2)+(1<<3)+(1<<5)+(1<<6)]=1;
  12. vis4[(1<<4)+(1<<5)+(1<<7)+(1<<8)]=vis4[(1<<5)+(1<<6)+(1<<8)+(1<<9)]=1;
  13. for(int i=0;i<(1<<10);i++)cnt[i]=cnt[i>>1]+(i&1);
  14. }
  15. inline int Get2(int i){
  16. return (1<<s[i])|(1<<s[i-1]);
  17. }
  18. inline int Get4(int i){
  19. return (1<<s[i])|(1<<s[i-1])|(1<<s[i-2])|(1<<s[i-3]);
  20. }
  21. struct node{
  22. int a0,a1,a2,a3;
  23. node(int _a0,int _a1,int _a2,int _a3){
  24. a0=_a0;a1=_a1;a2=_a2;a3=_a3;
  25. }
  26. inline bool operator <(const node &b)const{
  27. if(a0!=b.a0)return a0<b.a0;
  28. if(a1!=b.a1)return a1<b.a1;
  29. if(a2!=b.a2)return a2<b.a2;
  30. return a3<b.a3;
  31. }
  32. };
  33. inline bool check(int s,int N,int S){
  34. if(cnt[s]!=N)return 0;
  35. if((s&S)!=s)return 0;
  36. if(!vis4[S])return 0;
  37. return 1;
  38. }
  39. map<node,int> f,g;
  40. inline void solve(){
  41. scanf("%s",s+1);n=strlen(s+1);
  42. for(int i=1;i<=n;i++)s[i]-='0';
  43. f.clear();g.clear();f[node(-1,-1,-1,0)]=1;
  44. for(int i=1;i<=n;i++){
  45. for(auto it:f){
  46. node pre=it.first;
  47. for(int t=1;t<=9;t++){
  48. node T(pre.a1|(1<<t),pre.a2|(1<<t),pre.a3|(1<<t),-1);
  49. if(pre.a0!=-1&&(pre.a0|(1<<t))==Get4(i)&&vis4[Get4(i)])T.a3=0;
  50. if(T.a1!=-1&&T.a1==Get2(i)&&vis2[Get2(i)])T.a3=0;
  51. if(T.a2!=-1&&T.a2==(1<<s[i]))T.a3=0;
  52. if(T.a0!=-1&&(i==n||!check(T.a0,3,Get4(i+1))))T.a0=-1;
  53. if(T.a1!=-1&&(i>=n-1||!check(T.a1,2,Get4(i+2))))T.a1=-1;
  54. if((~T.a0)||(~T.a1)||(~T.a2)||(~T.a3))g[T]=(g[T]+it.second)%md;
  55. }
  56. }
  57. swap(g,f);g.clear();
  58. }
  59. int ans=0;
  60. for(auto it:f)if(!it.first.a3)ans=(ans+it.second)%md;
  61. printf("%d\n",ans);
  62. }
  63. int main(){
  64. init();
  65. scanf("%d",&T);
  66. while(T--)solve();
  67. return 0;
  68. }

[ARC068D] Solitaire

首先发现那个双端队列中的数一定先单调递减,然后再单调递增,最小值为 \(\text{1}\)。

现在考虑从双端队列中取数,那么当我们取到 \(\text{1}\) 这个数时,我们会在原来的双端队列中取到类似这样的两个数列:(分别用红、蓝表示)

那么红、蓝两数列的总长度为 \(k\),剩下的就是绿色的一段,长度为 \(n-k\),我们每次可以从两端任意取,共取 \(n-k-1\) 次,方案数为 \(2^{n-k-1}\) 。

所以我们只用考虑前 \(k\) 个数的取法,然后乘上 \(2^{n-k-1}\) 就好了。

由图像,我们看一下弹出序列需要满足什么条件:

首先第 \(k\) 位肯定是 \(\text{1}\)。(题目要求)

前 \(k\) 个数,一定是由一个或两个单调递减的数列混合而成的(这两个数列就是图中的红、蓝两个数列),并且其中的一个单调递减的数列肯定包含 \(\text{1}\) 这个点(蓝色数列)。

后 \(n-k\) 个数,其最大值应小于某一个提到的单调数列的最小值。也就是绿色段的最大值小于红色段的最小值。

那我们不妨设 \(f(i,j)\) 表示已经取了前 \(i\) 个数,他们的最小值为 \(j\) 时的方案数。(满足上面的 \(\text{3}\) 个条件)

那么我们设选的这 \(i\) 个数分成的两个单调递减的序列分别为 \(A\)、\(B\),其中最小值 \(j\) 在 \(A\) 中,\(B\) 中的最小值为 \(\text{minn}\)。

设除了我们选的这 \(i\) 个数剩下的 \(n-i\) 个数组成的集合为 \(S\),\(S\) 中最大的数为 \(\text{maxS}\)。

由 \(f(i,j)\) 的定义得,\(B\) 序列已经满足了第 \(\text{3}\) 个条件,即 \(\text{maxS}<\text{minn}\)。

我们现在要从 \(S\) 中选一个数,加入 \(A\) 或 \(B\) 里面。

考虑构造:

假设加入的数 \(x\) 满足 \(x<j\),那么我们就把它加到 \(A\) 的末尾,此时仍满足所有条件。所以 \(f(i,j)\) 向 \(f(i+1,1)\sim f(i+1,j-1)\) 转移。

假设加入的数 \(x\) 满足 \(x\geq j\),如果加入 \(A\) 中,\(A\) 就不满足单调递减的性质了,所以只能加入 \(B\) 中。

然后发现只能把 \(S\) 中的最大值 \(\text{maxS}\) 加入到 \(B\) 的队尾(而且必须得满足 \(\text{maxS}\geq j\),不然会在第一种情况中算重)。

由于 \(\text{maxS}\) 是 \(S\) 中的最大的数,所以当 \(\text{maxS}\) 加入 \(B\) 数列成为 \(B\) 数列的最小值后,仍大于 \(S\) 中的所有数,满足条件。

所以 \(f(i,j)\) 向 \(f(i+1,j)\) 转移。

至于为什么 \(S\) 中除 \(\text{maxS}\) 的某一个数 \(x\) 都不能加入 \(B\) 的队尾:因为加入后,\(B\) 中的最小值就变成了 \(x\),而 \(S\) 中的最大值仍为 \(\text{maxS}\)。此时 \(x<\text{maxS}\),不满足条件。

所以通过 \(f(i,j)\) 就可以向 \(f(i+1,1\sim j)\) 转移了。

显然答案就是 \(2^{n-k-1}\times\sum\limits_{i=2}^{n-k+2}f(k-1,i)\) 。

发现这等价于一个格路计数问题,可以用类似卡特兰数的方式做到线性。

点击查看代码
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,k;
  4. const int md=1e9+7;
  5. int fac[4005],inv[4005];
  6. inline void init(){
  7. fac[0]=fac[1]=inv[0]=inv[1]=1;
  8. for(int i=2;i<=n+k;i++)fac[i]=1ll*fac[i-1]*i%md;
  9. for(int i=2;i<=n+k;i++)inv[i]=1ll*(md-md/i)*inv[md%i]%md;
  10. for(int i=2;i<=n+k;i++)inv[i]=1ll*inv[i]*inv[i-1]%md;
  11. }
  12. inline long long C(int x,int y){
  13. if(!y)return 1;
  14. if(x<0||y<0||x<y)return 0;
  15. return 1ll*fac[x]*inv[y]%md*inv[x-y]%md;
  16. }
  17. inline long long pwr(int x,int y){
  18. int res=1;
  19. while(y>0){
  20. if(y&1)res=1ll*res*x%md;
  21. x=1ll*x*x%md;y>>=1;
  22. }
  23. return res;
  24. }
  25. int main(){
  26. scanf("%d%d",&n,&k);init();
  27. int tmp=1ll*((C(n+k-3,n-2)-C(n+k-3,n))%md+md)%md*pwr(2,n-k-1)%md;
  28. printf("%d\n",n==1&&k==1?1:tmp);
  29. return 0;
  30. }

[ARC056B] Range Argmax

[ABC221H] Count Multiset

[AGC004E] Salvage Robots

[Cnoi2019]青染之心

要有光

CF848D Shake It!

CF1667E Centroid Probabilities

多校联训 DP 专题的更多相关文章

  1. 多校联训 DS 专题

    CF1039D You Are Given a Tree 容易发现,当 \(k\) 不断增大时,答案不断减小,且 \(k\) 的答案不超过 \(\lfloor\frac {n}{k}\rfloor\) ...

  2. 决策单调性优化dp 专题练习

    决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队 ...

  3. 状压dp专题复习

    状压dp专题复习 (有些题过于水,我直接跳了) 技巧总结 : 1.矩阵状压上一行的选择情况 \(n * 2^n\) D [BZOJ2734][HNOI2012]集合选数 蒻得不行的我觉得这是一道比较难 ...

  4. 树形dp专题总结

    树形dp专题总结 大力dp的练习与晋升 原题均可以在网址上找到 技巧总结 1.换根大法 2.状态定义应只考虑考虑影响的关系 3.数据结构与dp的合理结合(T11) 4.抽直径解决求最长链的许多类问题( ...

  5. 区间dp专题练习

    区间dp专题练习 题意 1.Equal Sum Partitions ? 这嘛东西,\(n^2\)自己写去 \[\ \] \[\ \] 2.You Are the One 感觉自己智力被吊打 \(dp ...

  6. DP专题训练之HDU 2955 Robberies

    打算专题训练下DP,做一道帖一道吧~~现在的代码风格完全变了~~大概是懒了.所以.将就着看吧~哈哈 Description The aspiring Roy the Robber has seen a ...

  7. DP专题:划分数问题

    一.这个专题有什么用 练练DP 练练组合数学 ...... 二.正题 此类问题有如下几种形态: 1. 将n划分成若干正整数之和的划分数.2. 将n划分成k个正整数之和的划分数.3. 将n划分成最大数不 ...

  8. 【dp专题】NOIP真题-DP专题练习

    这里学习一下DP的正确姿势. 也为了ZJOI2019去水一下做一些准备 题解就随便写写啦. 后续还是会有专题练习和综合练习的. P1005 矩阵取数游戏 给出$n \times m$矩阵每次在每一行取 ...

  9. dp专题训练

    ****************************************************************************************** 动态规划 专题训练 ...

随机推荐

  1. echarts踩坑总结

    1,有关echarts引用的相关报错直接写这句  import * as echarts from 'echarts' 2,折线图 chartsObj = { tooltip: { trigger: ...

  2. Python最强IDE(PyCharm)安装教程

    欢迎关注公众号[Python开发实战],免费领取Python学习电子书! PyCharm是目前最流行.使用最广泛的Python IDE(Integrated Development Environme ...

  3. [题解] trip

    题目大意 给定一颗大小为 \(N\) 的树, \(1\)的度数不小于 \(2\) .每个点有一个颜色,要么为黑色要么为白色. 从 \(1\) 号点开始游走,计数器初始为 \(0\). 如果当前为黑点计 ...

  4. Wireshark抓包分析TCP“三次握手,四次挥手”

    1.目的 客户端与服务器之间建立TCP/IP连接,我们知道是通过三次握手,四次挥手实现的,但是很多地方对这个知识的描述仅限于理论层面,这次我们通过网络抓包的方式来看一下实际的TCP/IP传输过程. 2 ...

  5. 手脱FSG(2.0)

    1.查壳 2.x32dbg脱壳 在第二个xchg处使用ESP定律脱壳: 由于FSG壳特性,在跳转后位置向上查找,找到js\jne\jmp,jmp就是OEP位置: 在此处使用工具进行脱壳: 完成! 3. ...

  6. PyScript:让Python在HTML中运行

    大家好,我是DD,已经是封闭在家的第51天了! 最近一直在更新Java新特性和IDEA Tips两个专栏,其他方向内容的动态关注少了.昨天天晚上刷推的时候,瞄到了这个神奇的东西,觉得挺cool的,拿出 ...

  7. 难对齐、难保障、难管理?一文了解字节跳动如何解决数据SLA治理难题

    基于字节跳动分布式治理的理念,数据平台数据治理团队自研了SLA保障平台,目前已在字节内部得到广泛使用,并支持了绝大部分数据团队的SLA治理需求,每天保障的SLA链路数量过千,解决了数据SLA难对齐.难 ...

  8. .Net分表分库动态化处理

    介绍 本期主角:ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业务代码入侵 背景 最近有个小伙伴来问我,分表下他有一批数据,这个 ...

  9. 硬件开发笔记(四):硬件开发基本流程,制作一个USB转RS232的模块(三):设计原理图

    前者   前面建立好的基础的元器件,下面开始设计原理图.   需求 USB转RS232,输出RS232 可以选择性输出5V的TTL 可以选择性输出3.3V的TTL   设计原理图 步骤一:CH340G ...

  10. 面试常问的dubbo的spi机制到底是什么?

    前言 dubbo是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力.作为spring cloud alibaba体系中重要的一部分,随着spring cloud alibaba在 ...