Loading

cf2000左右dp

CF296B Yaroslav 和两个字符串 / Yaroslav and Two Strings

给定两个由数字和 ? 组成的字符串 \(s,t\),将 ? 替换为数字。若 \(s',t'\) 中有 \(s'_i>w'_i,s'_j<w'_j(1\leq i,j\leq n)\),则是一种合法的替换。求合法的方案数对 \(10^9+7\) 取模。\(1\leq n\leq 10^5\)

考虑 dp。设 \(f_{i,0/1,0/1}\) 表示第 \(i\) 位前有无 \(s_i>w_i\) 有无 \(s_i<w_i\)。转移直接枚举 \(s_i,w_i\) 的情况,\(f_{i,j|[c1>c2],k|[c1<c2]}=f_{i,j|[c1>c2],k|[c1<c2]}+f_{i-1,j,k}\)。注意初始 \(f_{0,0,0}=1\)。复杂度为大常数 \(O(n)\)aclink

#include<bits/stdc++.h>
#define i64 long long
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)

using namespace std;
const int N=1e5+5,M=1e9+7;

void solve();
int n;
i64 f[N][2][2];
char s[N],t[N];

signed main(){
  int Test=1;
//  scanf("%d",&Test);
  while(Test--) solve();
  return 0;
}

void solve(){
  scanf("%d",&n);
  scanf("%s%s",s+1,t+1);
  f[0][0][0]=1;
  L(i,1,n,1){
    L(x,0,1,1){
      L(y,0,1,1){
        L(j,0,9,1){
          L(k,0,9,1){
            if((s[i]=='0'+j||s[i]=='?')&&(t[i]=='0'+k||t[i]=='?')){
              f[i][x|(j>k)][y|(j<k)]=(f[i][x|(j>k)][y|(j<k)]+f[i-1][x][y])%M;
            }
          }
        }
      }
    }
  }
  printf("%lld\n",f[n][1][1]);
}

CF41D 士卒 / Pawn

给定一个矩阵,从底走到顶,每次可以向左上或右上移动,求路径上数字和最大且使 \(M\) 可整除,无解输出 -1\(1\leq n,m\leq 100,a_{i,j}\in [0,9]\)

如果没有模 \(k+1\) 的限制的话就是个朴素的二维 dp。有了限制考虑加上一维 \(k\) 表示和模 \(M\) 等于 \(k\)。所以 \(f_{i,j,k}=\max(f_{i,j,k},f_{i+1,j-1,p}+a_{i,j},f_{i+1,j+1,p}+a_{i,j})\),其中 \(p\) 是和去掉 \(a_{i,j}\) 的余数。还要求输出方案数,倒着推回去就行。复杂度 \(O(nmk)\)aclink

  • 注意 \(p=(k-a_{i,j}\bmod M+M)\bmod M\),以防止有负数。
  • 注意特判超出边界的各种情况。
#include<bits/stdc++.h>
#define i64 long long
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)

using namespace std;
const int N=105;

void solve();
int n,m,M,a[N][N],f[N][N][N];

signed main(){
  int Test=1;
//  scanf("%d",&Test);
  while(Test--) solve();
  return 0;
}

void solve(){
  scanf("%d%d%d",&n,&m,&M);
  M+=1;
  L(i,1,n,1){
    char s[N];
    scanf("%s",s+1);
    L(j,1,m,1) a[i][j]=s[j]-'0';
  }
  memset(f,255,sizeof f);
  L(j,1,m,1) f[n][j][a[n][j]%M]=a[n][j];
  R(i,n-1,1,1){
    L(j,1,m,1){
      L(k,0,M-1,1){
        int p=(((k-a[i][j])%M)+M)%M;
        if(j>1&&f[i+1][j-1][p]!=-1) f[i][j][k]=max(f[i][j][k],f[i+1][j-1][p]+a[i][j]);
        if(j<m&&f[i+1][j+1][p]!=-1) f[i][j][k]=max(f[i][j][k],f[i+1][j+1][p]+a[i][j]);
      }
    }
  }
  int x=0;
  L(j,1,m,1){
    if(f[1][j][0]>f[1][x][0]) x=j;
  }
  if(f[1][x][0]<0){
    printf("-1\n");
    return;
  }
  printf("%d\n",f[1][x][0]);
  int k=0,p=0,j=x;
  vector<char> ans;
  L(i,1,n-1,1){
    p=(((k-a[i][j])%M)+M)%M;
    if(j==1) ans.push_back('L'),j++;
    else if(j==m) ans.push_back('R'),j--;
    else if(f[i][j][k]==f[i+1][j-1][p]+a[i][j]) ans.push_back('R'),j--;
    else ans.push_back('L'),j++;
    k=p;
  }
  printf("%d\n",j);
  reverse(ans.begin(),ans.end());
  for(char c:ans) printf("%c",c);
}

CF362C 插入排序 / Insertion Sort

给定长度为 \(n\)\(0\)\(n-1\) 的排列 \(p\),对其进行冒泡排序。现可以在排序前先选择一对 \((i,j)\) 进行交换。问使操作后排序交换次数最小值,以及满足该条件的 \((i,j)\) 个数。\(2\leq n\leq 5000\)

显然不操作的最小次数就是逆序对个数。考虑枚举准备操作的位置 \((l,r)\),看看交换前后对答案有什么贡献。由于 \(l\) 换到了右边,原本所有的 \(a_i>a_l(l<i<r)\) 是没有贡献的,现在会产生贡献。小于的则不产生贡献,\(a_r\) 同理。我们只需要知道区间内大于小于 \(a_l,a_r\) 的有多少个。直接前缀和预处理定义 \(f_{i,j},g_{i,j}\) 分别为前 \(i\) 个数大于小于 \(j\) 的有多少个,则有 \(f_{i,j}=f_{i-1,j}+[a_i>j],g_{i,j}=g_{i-1,j}+[a_i<j>]\)。区间内的答案就是 \(s+(f_{r,a_l}-f_{l,a_l})-(g_{r,a_l}-g_{l,a_l})-(f_{r,a_r}-f_{l,a_r})+(g_{r,a_r}-g_{l,a_r})\)。最后取全局最小。复杂度 \(O(n^2)\)aclink

#include<bits/stdc++.h>
#define i64 long long
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)

using namespace std;
const int N=5e3+5;

void solve();
int n,sum,ans1=1<<30,ans2,a[N],f[N][N],g[N][N];

signed main(){
  int Test=1;
//  scanf("%d",&Test);
  while(Test--) solve();
  return 0;
}

void solve(){
  scanf("%d",&n);
  L(i,1,n,1) scanf("%d",a+i);
  L(i,1,n,1){
    L(j,0,n-1,1){
      f[i][j]=f[i-1][j]+(a[i]>j);
      g[i][j]=g[i-1][j]+(a[i]<j);
    }
  }
  L(i,1,n,1){
    L(j,i+1,n,1){
      if(a[i]>a[j]) sum++;
    }
  }
  L(i,1,n,1){
    L(j,i+1,n,1){
      int x=(f[j][a[i]]-f[i][a[i]])-(g[j][a[i]]-g[i][a[i]])
      -(f[j][a[j]]-f[i][a[j]])+(g[j][a[j]]-g[i][a[j]]);
      if(ans1>x){
        ans1=x;
        ans2=1;
      }
      else if(ans1==x) ans2++;
    }
  }
  printf("%d %d\n",sum+ans1,ans2);
}
posted @ 2025-11-02 22:27  jess1ca1o0g3  阅读(14)  评论(0)    收藏  举报