給你\(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; }