给你\(X+Y,X-Y\),求解\(X\)和\(Y\).c++
给你一个长度为\(n\)的串\(S\),询问有多少个子串\(T\)知足其中\(A,T\)数量相等,\(C,G\)数量相等.数组
用桶记录\(A-T\),\(C-G\)的数量,而后就没了.dom
n=rd();scanf("%s",S+1); int a=0,b=0;Mp[M][M]++; LL ans=0; for(int i=1; i<=n; ++i) { if(S[i]=='A')a++; else if(S[i]=='T')a--; if(S[i]=='C')b++; else if(S[i]=='G')b--; ans+=Mp[a+M][b+M]; Mp[a+M][b+M]++; }cout<<ans;
给你\(n\)条线段,每条线段左端点为\(A_i\),右端点为\(B_i\).若\(A_i\)或者\(B_i\)为\(-1\),则这个点未知,每一个点都不相同,只能取\(1,2..2n\).如今有一个要求就是若是线段相交,那么它们的长度需相等.询问是否存在一种状况知足要求.学习
考虑如何判断一个区间【L,R】合法.优化
那么咱们就能够用\(dp\)合并两个区间.(竟然没想到)spa
#include<bits/stdc++.h> using namespace std; const int M=405; int n; int A[M],B[M]; bool dp[M]; bool vis[M],flag[M]; bool Check(int l,int r) { if((r-l+1)&1)return false; int L=(r-l+1)/2; memset(flag,0,sizeof flag); for(int i=1; i<=n; ++i) { if(~A[i]&&~B[i]) { if(B[i]<l||A[i]>r)continue; if(A[i]<l||A[i]>L+l-1)return false; if(B[i]<L+l||B[i]>r)return false; if(B[i]-A[i]!=L)return false; }else if(~A[i]) { if(A[i]>r||A[i]<l)continue; if(A[i]>L+l-1)return false; if(vis[A[i]+L])return false; flag[A[i]+L]=true; }else if(~B[i]) { if(B[i]>r||B[i]<l)continue; if(B[i]<L+l)return false; if(vis[B[i]-L])return false; flag[B[i]-L]=true; } } for(int i=l; i<=r; ++i) if(!vis[i]&&!flag[i]) { if(i>l+L-1||vis[i+L]||flag[i+L])return false; flag[i+L]=true; } return true; } bool f2; int main(){ n=rd(); for(int i=1; i<=n; ++i) { A[i]=rd(),B[i]=rd(); if(~A[i]) { if(vis[A[i]]){return puts("No"),0;} vis[A[i]]=true; } if(~B[i]) { if(vis[B[i]]){return puts("No"),0;} vis[B[i]]=true; } }dp[0]=true; for(int i=1; i<=n<<1; ++i) for(int j=1; j<i; ++j) { if(!dp[j-1])continue; if(Check(j,i)){dp[i]=true;break;} } if(dp[n+n])puts("Yes"); else puts("No"); return 0; }
给你三个数\(n,k,m\),你能够选择\(1...n\)之间的数组成一个可重集,每种数最多有\(k\)个.询问对于每一个\(1\leq x\leq n\),使的这个数集的平均数为\(x\)的方案数.code
首先有一个很直观的思路,就是枚举平均数\(x\),那么数\(i\)对数集的影响就是\(i-x\),咱们就能够作一个小\(dp\),最后的答案就是\(dp[0]\),加一个前缀和优化,这样的复杂度大约\(O(n^4k)\).显然咱们能够来一个最值优化,那么确定跑不满,而后就水过去了.get
n=rd(),k=rd(),P=rd(); for(int i=1; i<=n; ++i) { int Mxr=0,Mxl=0,R=0; bool cur=0; dp[0][0]=1; for(int j=1; j<i; ++j)Mxl+=(i-j)*k; for(int j=n; j; --j) { R=min(Mxl,Mxr); for(int f=0; f<=R; ++f)dp[cur^1][f]=0; if(j==i) { for(int f=R; f>=0; --f)dp[cur^1][f]=1LL*dp[cur][f]*(k+1)%P; }else if(j>i) { for(int f=0; f<=R+(j-i)*k; ++f) { sum[f]=dp[cur][f]; if(f>=j-i)Add(sum[f],sum[f-(j-i)]); } for(int f=R+(j-i)*k; f>=0; --f) if(f>=(j-i)*(k+1))Add(dp[cur^1][f],sum[f]-sum[f-(j-i)*(k+1)]); else Add(dp[cur^1][f],sum[f]); }else { for(int f=R-(j-i)*k; f>=0; --f) { sum[f]=dp[cur][f]; if(f-(j-i)<=R-(j-i)*k)Add(sum[f],sum[f-(j-i)]); } for(int f=R-(j-i)*k; f>=0; --f) { if(f-(j-i)*(k+1)<=R-(j-i)*k)Add(dp[cur^1][f],sum[f]-sum[f-(j-i)*(k+1)]); else Add(dp[cur^1][f],sum[f]); } } if(j>=i)Mxr+=(j-i)*k; if(j<i)Mxl-=(i-j)*k; cur^=1; }printf("%d\n",(dp[cur][0]-1+P)%P); memset(dp,0,sizeof dp); }
(以上代码又丑又慢,请勿学习)咱们采用上面的思路发现,若是计算平均数为\(x\)的答案,那么\(1..n\)的数的贡献变成\(+1,+2,+3,+4,..+n-x\)和\(-1,-2,-3,-4,-(x-1)\),这时咱们惊奇地发现竟然是两个前缀.那么天然想到将正负贡献分开来,最后乘一下便可.it
n=rd(),k=rd(),P=rd(); int R=0; int MxR=n*(n+1)/2*k;dp[0][0]=1; for(int i=1; i<=n; ++i) { R+=k*i;R=min(R,MxR); for(int j=0; j<=R; ++j) { dp[i][j]=dp[i-1][j]; if(j>=i)Add(dp[i][j],dp[i][j-i]); } for(int j=R; j>=i*(k+1); --j) Add(dp[i][j],-dp[i][j-i*(k+1)]); }R=0; for(int i=1; i<=n; ++i) { int res=0; for(int j=0; j<=R; ++j) { Add(res,1LL*dp[i-1][j]*dp[n-i][j]%P); } R+=k*i;R=min(R,MxR); printf("%d\n",(1LL*res*(k+1)-1)%P); }
给你\(n\)个数\(A_1..A_n\),如今有一个数列\(X\),每一个\(X_i\)在\(1..A_i\)间随机生成.询问生成的数列\(X\)的指望\(LIS\)的长度.io
看到\(n\leq 6\),直接惧怕,这确定是一个奇奇妙妙的复杂度.我先暴力枚举这\(n\)个数的大小关系,而后咱们就获得一个肯定\(LIS\)的数列,统计知足这个大小关系的数列个数.
因而我一直在暴力用组合数推式子,结果啥也推不出.(题解:到这一步就是一道经典的分段值域\(dp\)问题[APIO2016]划艇).
也就是咱们能够将这些范围划分若干段,而后对于在同一范围内的数直接组合数求解.(具体看划艇这道题)
这道题还有个颇有意思的地方就是暴力枚举前面这些数的大小关系,毕竟有等于的状况比较难搞.看题解的时候看到一个叫贝尔数的东西.(具体看代码)
#include<bits/stdc++.h> using namespace std; const int P=1e9+7; int n; int A[10],bel[10]; int rk[10],tot; int p[10],f[10]; LL fpow(LL a,int cnt) { LL res=1; for(int i=cnt; i; i>>=1,a=a*a%P)if(i&1)res=res*a%P; return res; } void Add(int &x,int y) { x+=y;(x>=P)&&(x-=P); } int Ans; int s[10],b[10]; int dp[10][10]; LL C(int n,int m) { if(n<m)return 0; LL res=1; for(int i=n; i>n-m; --i)res=res*i%P; for(int i=2; i<=m; ++i)res=res*fpow(i,P-2)%P; return res; } void Solve() { for(int i=1; i<=tot; ++i)p[i]=i; do { for(int i=1; i<=n; ++i)rk[i]=p[bel[i]]; int ans=0; for(int i=1; i<=n; ++i) { f[i]=0; for(int j=1; j<i; ++j) if(rk[j]<rk[i])Max(f[i],f[j]); ++f[i];Max(ans,f[i]); }memset(s,0,sizeof s); for(int i=1; i<=n; ++i) { if(!s[rk[i]])s[rk[i]]=A[i]; else Min(s[rk[i]],A[i]); }b[tot]=s[tot]; for(int i=tot-1; i; --i)Min(s[i],s[i+1]),b[i]=s[i]; sort(b+1,b+tot+1);int cnt=unique(b+1,b+tot+1)-b-1; for(int i=1; i<=tot; ++i)s[i]=lower_bound(b+1,b+cnt+1,s[i])-b; memset(dp,0,sizeof dp); for(int i=0; i<=cnt; ++i)dp[0][0]=1; for(int i=1; i<=tot; ++i) { for(int j=1; j<=s[i]; ++j) { int len=b[j]-b[j-1]; for(int k=i; k; --k) if(s[k]>=j)Add(dp[i][j],dp[k-1][j-1]*C(len,i-k+1)%P); Add(dp[i][j],dp[i][j-1]); } for(int j=s[i]+1; j<=cnt; ++j)dp[i][j]=dp[i][j-1]; } Add(Ans,1LL*dp[tot][cnt]*ans%P); }while(next_permutation(p+1,p+tot+1)); } void dfs(int now) { if(now==n+1) {Solve();return;} for(int i=1; i<=tot; ++i) bel[now]=i,dfs(now+1); bel[now]=++tot;dfs(now+1);--tot; } int main(){ n=rd(); for(int i=1; i<=n; ++i)A[i]=rd(); dfs(1); for(int i=1; i<=n; ++i)Ans=1LL*Ans*fpow(A[i],P-2)%P; printf("%d",Ans); return 0; }
给你一个长度为\(n\)的数列\(X\),你能获得一个数列\(A\),知足\(1\leq A_i\leq X_i\).对于每一个\(A\)数列有一个\(P\)数列,\(P_i\)为\(i\)左边第一个比\(A_i\)大的数的下标,若没有则为\(-1\).询问有多少种不一样的\(P\)数列.
毫无思路,这至关于构建一棵笛卡尔树的过程,因此只要咱们能够统计出有多少种不一样形态的笛卡尔树就能够了.
考虑区间\(dp\),每次将两个区间和一个点合并,至关于建树.因为咱们须要保证这是一个大根堆,咱们还须要一维记录当前这个区间最大值不超过\(x\),看一下数据发现\(x\)有点大,可是咱们其实能够将它离散掉,那么复杂度为\(O(n^4)\).
#include<bits/stdc++.h> using namespace std; const int P=1e9+7; void Add(int &x,int y) { x+=y;(x>=P)&&(x-=P); } int n; int A[105]; int f[105][105][105]; int main(){ n=rd(); for(int i=1; i<=n; ++i) { A[i]=rd(),Min(A[i],n+1); for(int j=1; j<=n+1; ++j)f[i][i][j]=1; } for(int l=n; l; --l) for(int r=l+1; r<=n; ++r) for(int mid=l; mid<=r; ++mid) for(int k=1; k<=n+1; ++k) { int Mx=A[mid]>=k?k:A[mid]; int res=1; if(l^mid)res=1LL*res*f[l][mid-1][Mx]%P; if(r^mid)res=1LL*res*f[mid+1][r][Mx==n+1?Mx:Mx-1]%P; Add(f[l][r][k],res); } printf("%d\n",f[1][n][n+1]); return 0; }