文章目录

  • [CF296B 状态拆分](https://codeforces.com/problemset/problem/296/B)
  • CF362C
  • [CF229D dp+贪心记录辅助状态](https://codeforces.com/problemset/problem/229/D)
  • [CF223B 子序列匹配](https://codeforces.com/problemset/problem/223/B)
  • CF82D
  • [CF847E Packmen (二分+贪心)](https://codeforces.com/problemset/problem/847/E)
  • [CF222E Decoding Genome(矩阵快速幂优化dp)](https://www.luogu.com.cn/problem/CF222E)
  • [CF417D Cunning Gena 状压dp+排序技巧优化](https://www.luogu.com.cn/problem/CF417D)

CF296B 状态拆分

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//给定两个数字字符串 (含? 可以表示任意一个数字 )
//定义字符串s w不匹配为 : 存在 i,j s[i]>w[i] s[j]<w[j]
//问不匹配的情况一共有多少种 对1e9+7取模
char s[maxn],w[maxn];
ll dp[maxn][2][2];
//定义子问题:前i个数 s[j]>w[j] 是否存在 s[j]<w[j]是否存在 一共4种状态  可递推//dp[i][1/0][1/0]表示完成了前i位 (不)存在s[j]>w[j] 且(或) s[j] < w[j]的情况
//边界 dp[0][0][0]=1 dp[0][0][1]=dp[0][1][0]=dp[0][1][1]=0//转移过程就根据当且字符判断出当前状态可以由前一位的哪几个状态转移得到
int n;
int main()
{scanf("%d %s %s",&n,s+1,w+1);dp[0][0][0]=1;for(int i=1;i<=n;i++) {if(s[i]=='?' && w[i]=='?') {dp[i][0][0] = dp[i-1][0][0]*10 % mod;dp[i][0][1] = (dp[i-1][0][0]*45 % mod + dp[i-1][0][1]*55 % mod) % mod ; dp[i][1][0] = (dp[i-1][0][0]*45 % mod + dp[i-1][1][0]*55 % mod) % mod ; dp[i][1][1] = (dp[i-1][0][1]*45 % mod + dp[i-1][1][0]*45 % mod + dp[i-1][1][1]*100 % mod) % mod ;}else if(s[i]=='?' && w[i] != '?') {int d=w[i]-'0';dp[i][0][0] = dp[i-1][0][0];dp[i][0][1] = (dp[i-1][0][0]*d % mod + dp[i-1][0][1]*(d+1) % mod) % mod ; dp[i][1][0] = (dp[i-1][0][0]*(9-d) % mod + dp[i-1][1][0]*(9-d+1) % mod) % mod ; dp[i][1][1] = (dp[i-1][0][1]*(9-d) % mod + dp[i-1][1][0]*d % mod + dp[i-1][1][1]*10 % mod) % mod ;}else if(s[i]!='?' && w[i]=='?') {int d=s[i]-'0';d=9-d;dp[i][0][0] = dp[i-1][0][0];dp[i][0][1] = (dp[i-1][0][0]*d % mod + dp[i-1][0][1]*(d+1) % mod) % mod ; dp[i][1][0] = (dp[i-1][0][0]*(9-d) % mod + dp[i-1][1][0]*(9-d+1) % mod) % mod ; dp[i][1][1] = (dp[i-1][0][1]*(9-d) % mod + dp[i-1][1][0]*d % mod + dp[i-1][1][1]*10 % mod) % mod ;}else {int ds=s[i]-'0',dw=w[i]-'0';if(ds==dw){dp[i][0][0]=dp[i-1][0][0];dp[i][1][1]=dp[i-1][1][1];dp[i][1][0]=dp[i-1][1][0];dp[i][0][1]=dp[i-1][0][1];}else if(ds > dw) {dp[i][0][0]=0;dp[i][1][1]=(dp[i-1][1][1]+dp[i-1][0][1]) % mod;dp[i][0][1]=0;dp[i][1][0]=(dp[i-1][0][0]+dp[i-1][1][0]) % mod;}else {dp[i][0][0]=0;dp[i][1][0]=0;dp[i][1][1]=(dp[i-1][1][1]+dp[i-1][1][0]) % mod;dp[i][0][1]=(dp[i-1][0][0]+dp[i-1][0][1]) % mod;}}}cout<<dp[n][1][1]<<'\n';return 0;
}

CF362C

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 5e3+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//长度5e3的序列 任意交换一对位置后 逆序对最少可以为多少
//枚举逆序对 O(1)更新答案
int a[maxn];
int n;
int mmax[maxn][maxn],mmin[maxn][maxn];
//[i][j]区间大于a[i]  小于a[i]的个数
int cur=0;
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",a+i);for(int i=1;i<=n;i++) {for(int j=i+1;j<=n;j++) {if(a[j] > a[i]) {mmax[i][j]=mmax[i][j-1]+1;mmin[i][j]=mmin[i][j-1];}else{mmin[i][j]=mmin[i][j-1]+1;mmax[i][j]=mmax[i][j-1];cur++;//逆序对}}}for(int i=n;i>=1;i--) {for(int j=i-1;j>=1;j--) {if(a[i] > a[j]){mmin[i][j]=mmin[i][j+1]+1;mmax[i][j]=mmax[i][j+1];}else{mmax[i][j]=mmax[i][j+1]+1;mmin[i][j]=mmin[i][j+1];}}}//枚举每个逆序对int ans=INF;int cnt=0;for(int i=1;i<=n;i++) {for(int j=i+1;j<=n;j++) {if(a[i] < a[j]) continue;int t=cur-1+mmax[i][j-1]-mmin[i][j-1]+mmin[j][i+1]-mmax[j][i+1];if(ans==t) cnt++;else if(ans > t) ans=t,cnt=1;}}printf("%d %d\n",ans,cnt);return 0;
}

CF229D dp+贪心记录辅助状态

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 5e3+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//长度5e3的序列 可以合并某些子段成为1个数 使得序列成为非递减序列 问最多可以保留多少个数int a[maxn];
int n;
int dp[maxn];//前i个 答案
ll h[maxn];//前i个处理完 结尾的最小高度 贪心
ll sum[maxn];//前缀和
//求完dp[i-1] h[i-1]之后 对于dp[i] 我们要找到一个在i左边离i最近的位置j 使得[j+1,i]合并后大于等于h[j]
//[j+1,i-1]这段只能和i合并 因为如果和j合并就不符合贪心了
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",a+i);sum[i]=sum[i-1]+a[i];}h[0]=0;h[1]=a[1];dp[1]=0;dp[0]=0;for(int i=2;i<=n;i++) {for(int j=i-1;j>=0;j--) {if(h[j] <= sum[i]-sum[j]){//合并[j+1,i]dp[i]=dp[j]+i-j-1;h[i]=sum[i]-sum[j];break;}}}printf("%d\n",dp[n]);return 0;
}

CF223B 子序列匹配

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 2e5+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//给定字符串s t 对于每个s[i] 判断能否找到一个和t相等的子序列包括s[i]
//维护s[1~i]的所有子序列中 能匹配t的最长前缀位置 l[i]
//维护s[n~i]的所有子序列中 能匹配t的最长后缀位置 r[i]
//如果对于每个i 匹配到t的前缀和后缀有交集 那么就ok
char s[maxn],t[maxn];
int l[maxn],r[maxn];
int k=0;//匹配到前缀 后缀的位置  可知k是非递减的
int pos[30];
int lens,lent;
void solve(char *s1,char *s2,int *dp)
{fill(pos,pos+30,-1);//这个字符的上个位置 能匹配到的最长前缀k=1;for(int i=1;i<=lens;i++) {if(k<=lent && s1[i] == s2[k]){pos[s1[i]-'a']=k++;dp[i]=pos[s1[i]-'a'];}else dp[i]=pos[s1[i]-'a'];}
}
void solve1()
{fill(pos,pos+30,maxn);k=lent;for(int i=lens;i;i--){if(k && s[i] == t[k]) {pos[s[i]-'a']=k--;r[i]=pos[s[i]-'a'];}else r[i]=pos[s[i]-'a'];}
}
int main()
{scanf("%s %s",s+1,t+1);lens=strlen(s+1),lent=strlen(t+1);solve(s,t,l);solve1();for(int i=1;i<=lens;i++) {if(r[i] > l[i]) {puts("NO");return 0;}}puts("YES");return 0;
}

CF82D

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e3+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//n个人去银行排队 各自需要a[i]的时间 当排队人数大于等于3人 每次从前三个人当中 选2个 取最大的时间
//不足3人则直接取完  问处理完的最少总时间  n<=1000//当我们取完i轮 剩下的数一定是[1,2i+1]中任意一个数
//我们用剩下来的数作为状态 假设第i轮选完 剩下j  那么第i+1轮结束后只会剩下j/2i+2/2i+3中的一个
//输出决策 所以我们记录一下 当前当且这轮 是从三个里面选的哪两个 并且选的两个排后面
//dp[i+1][j]=min(dp[i+1][j],dp[i][j]+max(a[2i+2],a[2i+3]))
//dp[i+1][2i+2]=min(dp[i+1][2i+2],dp[i][j]+max(a[j],a[2i+3]))
//dp[i+1][2i+3]=min(dp[i+1][2i+3],dp[i][j]+max(a[j],a[2i+1]))
struct node
{int k;int i;int j;//pre[x][y]表示 第x轮选完 y没选 是从x-1轮的k没选状态转移而来 且x轮选了i j
}pre[maxn][maxn];
int a[maxn],n;
int dp[maxn][maxn];
void output(int x,int y) //第x轮 y剩下 这个状态的选择  输出pre[x][y].i j
{if(x>1) output(x-1,pre[x][y].k);if(pre[x][y].i >= n )//选到了初始的n之后的位置 也就是0  不需要输出{printf("%d\n",pre[x][y].j);return ;}if(pre[x][y].j >= n ){printf("%d\n",pre[x][y].i);return ;}printf("%d %d\n",pre[x][y].i,pre[x][y].j);
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",a+i);if(n==1) {printf("%d\n1\n",a[1]);return 0;}else if(n==2) {printf("%d\n1 2\n",max(a[1],a[2]));return 0;}a[++n]=0;for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=INF;dp[1][1]=max(a[2],a[3]),pre[1][1]={1,2,3};dp[1][2]=max(a[1],a[3]),pre[1][2]={1,1,3};dp[1][3]=max(a[1],a[2]),pre[1][3]={1,1,2};for(int i=2;i<=n/2;i++) {for(int j=1;j<=2*i-1;j++){//前i-1轮 剩下了j没选//j 2(i-1)+2 2(i-1)+3if(dp[i-1][j]+max(a[2*i+1],a[2*i]) < dp[i][j]) {dp[i][j]=dp[i-1][j]+max(a[2*i+1],a[2*i]);pre[i][j]=node{j,2*i+1,2*i};}if(dp[i-1][j]+max(a[2*i+1],a[j]) < dp[i][2*i]) {dp[i][2*i]=dp[i-1][j]+max(a[2*i+1],a[j]);pre[i][2*i]=node{j,2*i+1,j};}if(dp[i-1][j]+max(a[j],a[2*i]) < dp[i][2*i+1]) {dp[i][2*i+1]=dp[i-1][j]+max(a[j],a[2*i]);pre[i][2*i+1]=node{j,2*i,j};}}}printf("%d\n",dp[n/2][n]);output(n/2,n);//选完n/2轮 第n个没选 (也就是初始的第n+1个)return 0;
}

CF847E Packmen (二分+贪心)

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//给定字符串 .代表空 P代表人 *代表食物 人可以左右走 1秒走1格 问吃完食物需要多久
//二分 对于x秒 check能否吃完所有食物
//遍历每个人的位置 如果左边没有食物 就花x秒往右边吃
//如果左边有食物 要么吃完左边再往右边吃(最终停在右边) 要么先吃右边并留够时间吃左边(最终停在左边)//关键是每个人都是独立的 所以可以从左往右考虑食物是否被吃  对每个人的考量是单独贪心
int n;
int p[maxn],food[maxn];
int n1=0,n2=0;
char s[maxn];
bool check(int x)
{int cnt=0;//吃掉的食物个数for(int i=1;i<=n1 && cnt<n2;i++) {if(food[cnt+1]>p[i]) //当前这个人 左边已经没有食物了{while(cnt<n2 && food[cnt+1]<=p[i]+x) {cnt++;}}else {int k=p[i]-food[cnt+1];//把最左边没取的取了 所需的时间if(k>x) return 0;while(cnt<n2 && food[cnt+1]<=p[i]+max(0,x-2*k)) cnt++;//先左边 再右边while(cnt<n2 && 2*(food[cnt+1]-p[i])+k <= x) cnt++;//先右边 再左边}}return cnt==n2;
}
int main()
{scanf("%d %s",&n,s+1);for(int i=1;i<=n;i++) {if(s[i]=='P') p[++n1]=i;else if(s[i]=='*') food[++n2]=i;}int l=0,r=2*n,ans;while(l<=r) {int mid=l+r>>1;if(check(mid)) {ans=mid;r=mid-1;}else l=mid+1;}cout<<ans<<'\n';return 0;
}

CF222E Decoding Genome(矩阵快速幂优化dp)

某种DNA,长度不超过1e15,可以由'a''z','A''Z'52种字母组成,有k对(x,y)表示y不能出现在x的下一位,给出n m k,计算一共有多少种DNA序列。

首先不管n的范围想出一个dp递推式:

dp[n][j]表示长度为n,结尾是j字符的合法DNA数量

dp[n][j]=Σdp[n-1][k]*mp[k][j] (枚举k,如果k后面能接j,mp[k][j]==1,否则0)

这是一个线性的递推式,而且mp的范围不大,试试能否写成矩阵乘积的形式,

写出来发现是这样的:
某种DNA,长度不超过1e15,可以由'a''z','A''Z'52种字母组成,有k对(x,y)表示y不能出现在x的下一位,给出n m k,计算一共有多少种DNA序列。

首先不管n的范围想出一个dp递推式:

dp[n][j]表示长度为n,结尾是j字符的合法DNA数量

dp[n][j]=Σdp[n-1][k]*mp[k][j] (枚举k,如果k后面能接j,mp[k][j]==1,否则0)

这是一个线性的递推式,而且mp的范围不大,试试能否写成矩阵乘积的形式,

写出来发现是这样的:

最终结果就是左边矩阵元素之和,而由于初始矩阵每个元素都是1,所以我们求出转移矩阵的n-1次方,再把所有元素加起来就能得到最终答案。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define re register
const int maxn = 1e4 + 10;
const int mx = 105;
const int mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
//
ll n;
int m,k;
struct matr
{ll ax[55][55];void init(){memset(ax,0,sizeof ax);for(int i=0;i<55;i++) ax[i][i]=1;}matr operator *(const matr &f)const {matr ans;memset(ans.ax,0,sizeof ans.ax);for(int i=1;i<=m;i++){for(int j=1;j<=m;j++){for(int k=1;k<=m;k++){ans.ax[i][j]=(ans.ax[i][j]+ax[i][k]*f.ax[k][j]%mod)%mod;}}}return ans;}
}ei;
matr ksm(matr x,ll b)
{matr ret;ret.init();while(b) {if(b&1) ret=ret*x;x=x*x;b>>=1;}return ret;
}
inline int getid(char _)
{if('a'<=_ && _<='z') return _-'a'+1;else return _-'A'+27;
}
int mp[55][55];//不能连续出现的对
int main()
{scanf("%lld %d %d",&n,&m,&k);for(int i=1;i<=m;i++){for(int j=1;j<=m;j++) mp[i][j]=1;//初始都能出现}for(int i=1;i<=k;i++){char s[5];scanf("%s",s);int x=getid(s[0]),y=getid(s[1]);mp[x][y]=0;}for(int i=1;i<=m;i++){for(int j=1;j<=m;j++){ei.ax[i][j]=mp[j][i];//转移矩阵ax是mp的转置}}ei=ksm(ei,n-1ll);ll ans=0;for(int i=1;i<=m;i++){for(int j=1;j<=m;j++) ans=(ans+ei.ax[i][j])%mod;}cout<<ans<<'\n';return 0;}

CF1201D (贪心+dp)

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define re register
const int maxn = 2e5 + 10;
const int mx = 105;
const ll mod = 1e9+7;
const ll inf = (ll)1e16;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
//给定n*m网格 某些格子有宝藏 初始在左下角只能通过某些特殊列上去 求拿完所有宝物最短时间
int n,m,k,q;
int l[maxn],r[maxn];//第i列 最左边 最右边的宝藏列数
int up[maxn];
int h;//实际爬的最高高度
//每一行只需要考虑最左边以及最右边的宝藏  拿完之后要么停在最左边要么停在最右边
//上去的决策 要么从左边最近的特殊列 要么从右边最近的特殊列
//上去之后先拿右边停在左边  先拿左边停在右边
//2*2*2 8种决策
//变量初始化成inf 可以使得某些无法作出的决策不被选到
int main()
{scanf("%d %d %d %d",&n,&m,&k,&q);fill(l,l+maxn,maxn);for(int i=1;i<=k;i++) {int x,y;scanf("%d %d",&x,&y);l[x]=min(l[x],y);r[x]=max(r[x],y);h=max(h,x);}for(int i=1;i<=q;i++) scanf("%d",up+i);sort(up+1,up+q+1);if(!r[1]) l[1]=r[1]=1;//如果第一列没宝藏 可以假设起点是宝藏ll ansl=r[1]-1+r[1]-l[1],ansr=r[1]-1;int lastx=l[1],lasty=r[1];for(int i=2;i<=n;i++) {if(!r[i]) continue;//这一行没有宝藏就直接走//从lastx 左右两列上去  从lasty 左右两列上去ll xl=inf,xr=inf,yl=inf,yr=inf;//先预设为inf  如果不存在就不会被选到ll al=inf,ar=inf;//拿完本层的花费int idxr=lower_bound(up+1,up+q+1,lastx)-up;if(idxr>1) xl=up[idxr-1];if(idxr<=q) xr=up[idxr];int idyr=lower_bound(up+1,up+q+1,lasty)-up;if(idyr>1) yl=up[idyr-1];if(idyr<=q) yr=up[idyr];//lastx->xl->r[i]->l[i]al=min(al ,ansl+abs(xl-lastx)+abs(r[i]-xl)+abs(r[i]-l[i]));//lastx->xl->l[i]->r[i]ar=min(ar ,ansl+abs(xl-lastx)+abs(l[i]-xl)+abs(r[i]-l[i]));//lastx->xr->l[i]->r[i]ar=min(ar ,ansl+abs(xr-lastx)+abs(l[i]-xr)+abs(r[i]-l[i]));//lastx->xr->r[i]->l[i]al=min(al ,ansl+abs(xr-lastx)+abs(r[i]-xr)+abs(r[i]-l[i]));//lasty->yl->r[i]->l[i]al=min(al ,ansr+abs(yl-lasty)+abs(r[i]-yl)+abs(r[i]-l[i]));//lasty->yl->l[i]->r[i]ar=min(ar ,ansr+abs(yl-lasty)+abs(l[i]-yl)+abs(r[i]-l[i]));//lasty->yr->l[i]->r[i]ar=min(ar ,ansr+abs(yr-lasty)+abs(l[i]-yr)+abs(r[i]-l[i]));//lasty->yr->r[i]->l[i]al=min(al ,ansr+abs(yr-lasty)+abs(r[i]-yr)+abs(r[i]-l[i]));ansl=al,ansr=ar;lastx=l[i],lasty=r[i];}printf("%lld\n",min(ansl,ansr)+h-1);return 0;
}

CF417D Cunning Gena 状压dp+排序技巧优化

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define re register
const int maxn = 1e2 + 10;
const int mx = 105;
const ll mod = 1e9+7;
const ll inf = (ll)4e18+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
//n个人解决m个题目 每人可以解决特定题目 雇佣代价为ki 且要求需要xi个显示器 每个显示器价格相等
//n<=100    m<=20//先不考虑显示器 用状压dp dp[sta]表示解决了sta状态且不考虑显示器的最小代价
//dp[now|last]=min(dp[now|last],dp[last]+ki)//我们把n个人按所需显示器数量升序排序 //每到达一次最终状态 就用当前的人所需的显示器去更新答案
//由于我们显示器是升序排的  所以最后这个人需要的显示器一定是最大的struct node
{ll x,k;int pro;//状压
}p[maxn];
inline bool cmp (const node &a,const node &b)
{if(a.k != b.k)return a.k<b.k;else return a.x<b.x;
}
ll dp[1<<20];
int n,m,b;
int main()
{scanf("%d %d %d",&n,&m,&b);for(int i=1;i<=n;i++) {int a;scanf("%lld %lld %d",&p[i].x,&p[i].k,&a);p[i].pro=0;for(int j=1;j<=a;j++){int d;scanf("%d",&d);d--;p[i].pro |= (1<<d);}}sort(p+1,p+n+1,cmp);//排序 for(int i=1;i<(1<<m);i++) dp[i]=inf;ll ans=inf;for(int j=1;j<=n;j++) //先枚举每个人{for(int i=0;i<(1<<m);i++) //后枚举状态 由已知状态去更新未知状态{if(dp[i] != inf) {int now=i|p[j].pro;dp[now]=min(dp[now],dp[i]+p[j].x);if(dp[(1<<m)-1] != inf) {ans=min(ans,dp[(1<<m)-1]+p[j].k*b);               }}}}if(ans == inf) ans=-1;cout<<ans<<'\n';return 0;
}

C 操作集锦(子序列问题)

给定字符串,求长度为k的子序列有多少种。
(1<=n,k<=1000)

这题按道理是很简单的一题,主要是关于子序列匹配问题有一个技巧,就是记录当前元素的最近一次出现的位置。

dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
如果s[i]上一次出现在pre[i]这个位置,那么就需要减去重复计算的部分,也就是pre[i]之前的与pre[i]构成的子序列。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e3 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
ll dp[maxn][maxn];//前i个字符中 长度为j的子序列个数
//dp[i][j]=dp[i-1][j-1]+dp[i-1][j] - dp[pre[i]-1][j-1] 减去重复的部分
int n,k;
char s[maxn];
int pos[256],pre[maxn];
int main()
{scanf("%d %d",&n,&k);scanf("%s",s+1);for(int i=1;i<=n;i++){pre[i]=pos[s[i]];pos[s[i]]=i;}dp[0][0]=1;for(int i=1;i<=n;i++){dp[i][0]=1;for(int j=1;j<=n;j++) {dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%INF;if(pre[i]) dp[i][j]=(dp[i][j]-dp[pre[i]-1][j-1]+INF)%INF;}}cout<<dp[n][k];return 0;
}

cf1900左右的dp相关推荐

  1. dp,sp,px相互转化

    方法一: public int sp2px(float sp) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, ...

  2. [JS][dp]题解 | #打家劫舍(一)#

    题解 | #打家劫舍(一)# 题目链接 打家劫舍(一) 题目描述 描述 你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家, ...

  3. HDU 2084 数塔(DP)(JAVA版)

    数塔 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  4. dp cf 20190615

    A. Timofey and a tree 这个不算是dp,就是一个思维题,好难想的思维题,看了题解才写出来的, 把点和边分开,如果一条边的两个点颜色不同就是特殊边,特殊边两边连的点就叫特殊点, 如果 ...

  5. BZOJ 1003[ZJOI2006]物流运输(SPFA+DP)

    Problem 1003. -- [ZJOI2006]物流运输 1003: [ZJOI2006]物流运输 Time Limit: 10 Sec  Memory Limit: 162 MB Submit ...

  6. [NOI2005]聪聪与可可(期望dp)

    题意:给一张无向图,有一只猫和一只老鼠,猫每秒会向老鼠的方向移动两个单位,若它们的距离为一,那么只会移动一个单位,老鼠会等概率向周围移动一步或不动,求猫抓到老鼠的期望时间. Solution luog ...

  7. Codeforces 903F Clear The Matrix(状态压缩DP)

    题目链接 Clear The Matrix 题意 给定一个$4 * n$的矩形,里面的元素为$'.'$或$'*'$.现在有$4$种正方形可以覆盖掉$'*'$,正方形的边长分别为$1,2,3,4$. 求 ...

  8. 喵哈哈村的魔法考试 Round #1 (Div.2) 题解源码(A.水+暴力,B.dp+栈)

    A.喵哈哈村的魔法石 发布时间: 2017年2月21日 20:05   最后更新: 2017年2月21日 20:06   时间限制: 1000ms   内存限制: 128M 描述 传说喵哈哈村有三种神 ...

  9. 尼克的任务 dp 洛谷1280

    蒟蒻表示老久没看过dp题目了,,挺水的一道dp题目都没想出来,,, 首先设dp[i]表示从开始到i时间的最大空闲时间,用vector to[x] 表示从x点开始的任务结束时间,cnt[x]表示从x开始 ...

最新文章

  1. 原创:检查点的三种加入方式
  2. NET问答: 如何取消或中止 Task 执行 ?
  3. LYNC文件传输功能开关
  4. ABP动态生成WebAPI
  5. flume学习(一):log4jAppender直接输出日志到flume的avro-source
  6. Android Native内存泄露检测(针对Android7.0)
  7. 十天学会php chm,【div+css】十天学会div+css---第一天_html/css_WEB-ITnose
  8. Electron 设置透明窗口transparent 属性win7无效详解
  9. 如何下载msdn上面的操作系统镜像
  10. 软件测试术语 - 需求跟踪矩阵
  11. Django1.9重写用户模型报错has no attribute 'USERNAME_FIELD'
  12. hdu 4272 LianLianKan
  13. 云服务 saas_SaaS和云服务策划
  14. linux ioctl网络参数设置,Linux 网络编程之ioctl函数
  15. 收购游戏手机厂商黑鲨背后,腾讯走了一步好棋?
  16. 【SWAT水文模型】SWAT水文模型建立及应用第四期: 气象数据的准备(传统气象站)(待更新)
  17. Python通过Spleeter实现音唱人声(歌声)伴奏分离
  18. 详解美国四大OTT电视直播服务
  19. 一万左右启动资金,做什么生意比较合适?
  20. MT6795详细芯片资料下载  MT6795规格说明简介

热门文章

  1. 2022年货节有什么数码家电推荐的,2022年货节数码家电购物清单
  2. 【计算机网络】抓取路由器包
  3. 5G已来,“分”尽其用!跟考生聊一聊填报通信行业志愿的事儿~
  4. Linux Centos8系统 修改 /etc/profile文件,添加java相关的环境变量
  5. 元旦假期,回到村里,生活不易!
  6. 在外国影响深远的家族,381年后回到中国认祖归宗,族谱:我是中国人
  7. 徐老师家的螃蟹肥了~
  8. java-net-php-python-jspm高校食堂点餐系统演示录像2019计算机毕业设计程序
  9. 浅析AI人脸识别/车辆识别智能分析网关的深度学习算法及应用场景
  10. 阿里云NAS文件系统实现跨账号挂载