2019年到了,馬上又是省賽區域賽的一年了,但是我的dp水平還是堪憂,所以在衆多網站(主要codeforce)蒐羅一下dp的好題刷一下,並在此發一下解題報告來督促自己....
目錄
- MemSQL Start[c]UP 3.0 - Round 1 C.Pie Rules
- Avito Cool Challenge 2018 C. Colorful Bricks(組合數學+dp)
- Codeforces Round #538 (Div. 2) D. Flood Fill
- AtCoder Beginner Contest 118 D - Match Matching
- Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost(樹上dp)
- Codeforces Round #518 (Div. 1) [Thanks, Mail.Ru!] A. Array Without Local Maximums(計數dp)
- [NOIP2018模擬賽] 小P的技能(計數dp+構造二叉樹)
- Codeforces Global Round 1 D. Jongmah(思維+巧妙DP)
- Hello 2019 D. Makoto and a Blackboard(概率dp+積性函數)
- Comparing Your Heroes ZOJ 2002(狀壓dp+拓撲思維)
- Codeforces Round #427 (Div. 2) D. Palindromic characteristics(區間dp+hash)
- Misunderstood … Missing 2018 Ec-final 西安(二維01揹包)
- Educational Codeforces Round 60 (Rated for Div. 2) D. Magic Gems(dp+矩陣快速冪優化)
- Codeforces Round #548 (Div. 2) D.Steps to On(期望DP+莫比烏斯反演)
- 浙江師範大學校賽 G.Little Sub and Piggybank(動態規劃)
- 山東省第十屆省賽 B.Flipping Game(DP)
- Educational Codeforces Round 64 (Rated for Div. 2)(換根dp)
- Atcoder AGC 030D Inversion Sum(期望dp)(好題)
MemSQL Start[c]UP 3.0 - Round 1 C.Pie Rules題意:有n個派,每個派有一個大小,Alice和Bob有一個令牌,誰有令牌可以選擇當前派給自己或對方,同時下一回合沒有得到派的人將得到令牌,已知每個人都會選擇最優的策略,Alice和Bob最終得到的派的體積分別是多少? 題解:因爲第一回合是Bob擁有令牌,我們設dp[i]代表第i回合選擇的人的最大價值,那麼dp[1]就是Bob的答案,然後逆推進行轉移就可以了,很不錯的dp思維題~~ |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int a[maxn],dp[maxn],sum[maxn];
int Sheryang(){
int n=read;
for(int i=1;i<=n;i++){
a[i]=read;
}
for(int i=n;i>=1;i--){
sum[i]=sum[i+1]+a[i];
dp[i]=max(dp[i+1],sum[i+1]-dp[i+1]+a[i]);
}
printf("%d %d\n",sum[1]-dp[1],dp[1]);
return 0;
}
Avito Cool Challenge 2018 C. Colorful Bricks(組合數學+dp)
題意:n個方格m種顏色,要求有k個方格與左邊的方格顏色不一樣的方案數。
題解:比較簡單的計數dp,dp[i][j]爲前i個方格有j種不同的方案數,枚舉到第i位的時候只有兩種狀態,和左邊一樣dp[i][j]+=dp[i-1][j]和左邊不一樣dp[i][j]+=dp[i-1][j-1]*(m-1)即可~~~
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll dp[2005][2005];
void add(ll &x,ll y){
x+=y;
if(x>=mod) x-=mod;
}
int Sheryang(){
int n=read,m=read,K=read;
dp[1][0]=m;
for(int i=2;i<=n;i++){
for(int j=0;j<=min(K,i-1);j++){
add(dp[i][j],dp[i-1][j]);
if(j) add(dp[i][j],dp[i-1][j-1]*(m-1)%mod);
//printf("%d %d = %lld\n",i,j,dp[i][j]);
}
}
printf("%lld\n",dp[n][K]);
return 0;
}
Codeforces Round #538 (Div. 2) D. Flood Fill
題意:相鄰方塊顏色顏色一樣則可成爲一個聯通塊,每次可以把一個聯通塊變爲相鄰聯通塊的顏色,最小變換多少次使得所有方塊都具有同一顏色。
題解:剛開始的方塊左右兩邊可能和自己是同一顏色,沒法直接進行區間dp,所以首先要將相同顏色的聯通塊壓成一塊,然後直接進行經典的區間dp就可以了~~(想到壓縮也想到區間dp,但是沒想到壓縮然後區間dp,卡了一段時間才過,蠢死~)
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
#define pi acos(-1)
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/* keep hungry and keep calm! */
int a[maxn],c[maxn],n,dp[5005][5005];
int dfs(int l,int r){
if(dp[l][r]!=-1) return dp[l][r];
int ans=0;
if(l==r) return 0;
if(a[l]==a[r]) return ans=dfs(l+1,r-1)+1;
else ans=min(dfs(l,r-1),dfs(l+1,r))+1;
dp[l][r]=ans;return ans;
}
int Sheryang(){
n=read;
for(int i=1;i<=n;i++){
c[i]=read;
}
a[1]=c[1];
int cnt=1;
for(int i=2;i<=n;i++){
if(a[cnt]==c[i]) continue;
a[++cnt]=c[i];
}
memset(dp,-1,sizeof(dp));
printf("%d\n",dfs(1,cnt));
return 0;
}
AtCoder Beginner Contest 118 D - Match Matching
- 題意:有n根火柴,m種數字,數字1,2,3,4,5,6,7,8,9分別需要2,5,5,4,5,6,3,7,6根火柴,要求n根火柴全部都用完且拼成的數字最大,輸出這個數字。
- 題解:簡單的完全揹包問題,先dp得出拼成的數字的最大位數然後將dp倒回去查找路徑,查找路徑的過程中可以貪心從最大的數字開始考慮。
- Noting:dp數組需要一開始初始化爲-1,否則會dp出一個比答案大的值!!!
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int s[]={0,2,5,5,4,5,6,3,7,6},a[maxn],ans[maxn],n,m,dp[maxn];
int cmp(int a,int b){
if(s[a]!=s[b]) return s[a]<s[b];
return a>b;
}
int cmp1(int a,int b){
return a>b;
}
int Sheryang(){
n=read,m=read;
for(int i=0;i<m;i++){
a[i]=read;
}
memset(dp,-1,sizeof(dp));
dp[0]=1;
int vis[10]={0};
for(int i=0;i<m;i++){
for(int j=s[a[i]];j<=n;j++){
dp[j]=max(dp[j],dp[j-s[a[i]]]+1);
}
}
sort(a,a+m,cmp1);
while(n){
for(int i=0;i<m;i++){
if(n>=s[a[i]] && dp[n-s[a[i]]]+1==dp[n]){
putchar(a[i]+'0');
n=n-s[a[i]];break;
}
}
}
puts("");
return 0;
}
Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost(樹上dp)題意:給出一顆有n個節點樹以及n個節點的權值,定義一顆以rt爲根的樹的價值爲每一個節點到根的路徑長度*節點的權值,你可以以任意節點爲根,求樹的最大價值. 題解:首先我們先以節點1爲根,求出dp[i],表示i節點的子樹到i的路徑長度*權值,維護一個樹上的權值後綴和數組即可轉移,然後再進行一次dfs,考慮從u-v 即以u爲根的樹的價值如何轉移到以v爲根的樹的價值,首先v節點的子樹深度都會減少一層,然後u除v的子樹以外的子樹深度都增加一層,那麼轉移即可(感覺很清晰 經典的樹上dp呀) |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int a[maxn];
ll dp[maxn],sum[maxn];
vector<int>G[maxn];
void dfs1(int u,int fa){
sum[u]=a[u];
int sz=G[u].size();
for(int i=0;i<sz;i++){
int v=G[u][i];
if(v==fa) continue;
dfs1(v,u);
sum[u]+=sum[v];
dp[u]+=dp[v]+sum[v];
}
}
void dfs2(int u,int fa){
int sz=G[u].size();
for(int i=0;i<sz;i++){
int v=G[u][i];
if(v==fa) continue;
dp[v]=dp[u]-sum[v]+(sum[1]-sum[v]);
dfs2(v,u);
}
}
int Sheryang(){
int n=read;
for(int i=1;i<=n;i++){
a[i]=read;
}
for(int i=0;i<n-1;i++){
int u=read,v=read;
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0);
printf("%lld\n",*max_element(dp+1,dp+1+n));
return 0;
}
Codeforces Round #518 (Div. 1) [Thanks, Mail.Ru!] A. Array Without Local Maximums(計數dp)題意:給出一個數組,其中有幾項爲-1代表沒有數字,你可以在裏面填數字(1-200),要求滿足a[1]<=a[2] && a[i]<=max(a[i-1],a[i+1]) && a[n]>=a[n-1] 的方案數 題解:典型的計數dp,對於第i位你只需要枚舉這一位選什麼數字以及這一位與i-1位的關係即可,dp[i][j][k]代表第i位選j且與i-1位的關係k的方案數。 |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e5+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll dp[maxn][205][3],a[maxn]; /// a[i-1]與a[i] < = >
int Sheryang(){
ll n=read;
for(int i=1;i<=n;i++){
a[i]=read;
}
for(int i=1;i<=200;i++){
if(a[1]==-1 || a[1]==i){
dp[1][i][0]=1;
}
}
for(int i=2;i<=n;i++){
ll tmp=0; /// a[i-1]<a[i]
for(int j=1;j<=200;j++){
if(a[i]==j || a[i]==-1){
dp[i][j][0]=tmp;
dp[i][j][1]=dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2]; ///a[i]==a[i-1]
}
tmp+=dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2];
tmp%=mod;
}
tmp=0;/// a[i-1]>a[i]
for(int j=200;j>=1;j--){
if(a[i]==j || a[i]==-1){
dp[i][j][2]=tmp;
}
tmp+=dp[i-1][j][1]+dp[i-1][j][2];
tmp%=mod;
}
}
ll ans=0;
for(int i=1;i<=200;i++){
ans+=dp[n][i][1]+dp[n][i][2];
ans%=mod;
}
cout<<ans<<endl;
return 0;
}
[NOIP2018模擬賽] 小P的技能(計數dp+構造二叉樹)
題意:一顆無限大的二叉樹,選擇n個節點,要求選擇一個節點必先選擇父節點,問能選擇到第k層節點的方案數。
題解:神仙dp啊!!!一開始的思路dp[i][j]選擇j個節點能選到第i層的方案數,但是太難了....實在是不會,看過標稱後發現,正解的dp是dp[i][j]選擇j個節點不能選到第j層的方案數....經過Tangjz老師的指點,dp[i][j]意思是選擇j個節點深度小於i的二叉樹的數量,考慮一顆經過根節點的二叉樹,根節點左側樹的大小爲u,根節點右側樹的大小爲v,且層數都小於i,那麼刪掉根節點後就可以得到兩個使用節點分別是u和v且深度小於i-1的二叉樹,兩顆子樹相互不影響那麼dp[i][u+v+1]就可以這麼轉移過來.....
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll f[505][505]; ///f[i][j] 層數小於i點數爲j的二叉樹的個數
int Sheryang(){
int n=read,k=read;
for(int i=1;i<=n+1;i++){
f[i][0]=1;
}
for(int i=2;i<=n+1;i++){
for(int j=1;j<=n;j++){
for(int k=0;k<j;k++){
f[i][j]+=f[i-1][k]*f[i-1][j-k-1];
f[i][j]%=mod;
}
}
}
ll Ans=(f[n+1][n]-f[k][n]+mod)%mod;
printf("%lld\n",Ans);
return 0;
}
Codeforces Global Round 1 D. Jongmah(思維+巧妙DP)題意:給出n個在m以內數字,每個數字可以構成[i,i,i] [i-1,i,i+1]的三元組,每個數只能組成一個三元組,問最優組成多少三元組? 題解:這個題有點不太好想,轉移什麼的真的很巧妙,或許這就是dp的真諦吧.... 首先有一個結論,對於一個數x的遞增三元組不會超過二個,否則可以轉換成三個三張牌相同的三元組,答案是同樣優的,對於這個dp我們可以設計三個狀態dp[i][j][k]表明考慮數字i時,存在j個以i開頭的遞增三元組,k個以i做爲中間元素的遞增三元組.那麼便開始考慮從第dp[i-1][k][l] -> dp[i][j][k] 只需要在後面補上幾個數字便可以得到轉移,同時數字i用了j+k+l個,剩下的可以全部變成[i,i,i]的三元組 |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int f[maxn][3][3],cnt[maxn];
int Sheryang(){
int n=read,m=read;
memset(f,-1,sizeof(f));
for(int i=1;i<=n;i++){
int x=read;
cnt[x]++;
}
f[0][0][0]=0;
for(int i=1;i<=m;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
for(int l=0;l<3;l++){
if(cnt[i]>=j+k+l){
f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+j+(cnt[i]-j-k-l)/3);
}
}
}
}
}
printf("%d\n",f[m][0][0]);
return 0;
}
Hello 2019 D. Makoto and a Blackboard(概率dp+積性函數)題意:開始你手裏有一個數字n,每一回合它都會等概率的變成它的一個因子,問k回合後這個數字大小的期望. 題解:首先對於這個期望可以猜一下,這是個積性函數....(爲啥?我也不知道...要是有大佬知道可以告訴我一下),那麼對於積性函數,總期望=每個質因子的的期望的乘積,那麼對於每個質因子進行概率dp,dp[i][j]對於某一個質因子來說第i輪時冪次爲j的概率是多少,可以枚舉第i-1輪的時候的冪次k,轉移的概率是1/(k+1),最後再根據概率處理出期望即可..... |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e5+7;
typedef long long ll;
const int mod=1e9+7;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/* keep hungry and keep calm! */
ll dp[(int)1e4+7][62],n,k,inv[62];
ll qpow(ll a,ll b){
ll ans=1;a%=mod;
while(b){
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;b/=2;
}
return ans;
}
ll solve(ll p,int e){
dp[0][e]=1;
for(int i=0;i<e;i++) dp[0][i]=0;
for(ll i=1;i<=k;i++){
for(int j=0;j<=e;j++){
dp[i][j]=0;
for(int k=j;k<=e;k++){
dp[i][j]=(dp[i][j]+dp[i-1][k]*inv[k+1]%mod)%mod;
}
}
}
ll tmp=0,t=1;
for(int i=0;i<=e;i++){
tmp=(tmp+t*dp[k][i]%mod)%mod;
t=(t*p)%mod;
}
return tmp;
}
int Sheryang()
{
inv[1]=1;
for(int i=2;i<=60;i++){
inv[i]=qpow(i,mod-2);
}
n=read,k=read;
ll ans=1;
for(ll i=2;i*i<=n;i++){
int cot=0;
while(n%i==0) cot++,n/=i;
if(cot) ans=(ans*solve(i,cot)%mod)%mod;
}
if(n>1) ans=(ans*solve(n,1)%mod)%mod;
printf("%lld\n",ans);
return 0;
}
Comparing Your Heroes ZOJ 2002(狀壓dp+拓撲思維)題意:給你n對拳皇的名字,代表誰比誰強,要求你根據這個給出現的人物排序,一共有多少種排序的方法? 題解:首先應該能想到一種計數dp,但就沒有更深入的想法了....但是我們可以首先將給我們的點對進行連邊,這樣就變成了一個有向圖,我們可以想從前往後進行安排,首先可以安排哪些人?應該是哪些沒有入度的點,因爲沒有人比他們強,那我們延伸一下,若存在一條邊u->v,則u一定在v之前安排,那麼對於一個點v,只要它的前綴點全部出現在一個排列中,那麼就可以安排它,處理這種排練的是狀壓dp. |
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int dp[maxn],pre[maxn],cnt;
char s[maxn];
int Sheryang()
{
int n;
while(~scanf("%d",&n)){
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
map<string,int>mp;
cnt=0;
for(int i=1;i<=n;i++){
scanf("%s",s);
int u,v;
if(!mp.count(s)){
mp[s]=cnt++;
}
u=mp[s];
scanf("%s",s);
if(!mp.count(s)){
mp[s]=cnt++;
}
v=mp[s];
pre[v]|=1<<u;
}
dp[0]=1;
for(int i=0;i<(1<<cnt);i++){if(dp[i])
for(int j=0;j<cnt;j++){
if(!(i&(1<<j)) && (i&pre[j])==pre[j]){
dp[i|(1<<j)]+=dp[i];
}
}
}
printf("%d\n",dp[(1<<cnt)-1]);
}
return 0;
}
Codeforces Round #427 (Div. 2) D. Palindromic characteristics(區間dp+hash)題意:給你一個字符串,定義k-迴文串的含義是對於長度爲n的迴文字符串,左邊長度n/2的子串和右邊長度n/2的子串是(k-1)-迴文串,輸出分別有多少個k-迴文串 題解:很容易可以想到一種區間dp的做法,因爲對於一個區間只有一個轉移,所以總體複雜度n*n,但是分出區間之後還要判斷左右兩半翻轉過來是否相等,這個可以用hash來判斷,求一個前綴hash和一個後綴hash,若是迴文串,則dp[i][j]表示從i到j最大能形成編號爲多少的迴文串,dp[i][j]=min(dp[i][i+len/2-1],dp[j-len/2+1][j])+1,轉移即可,但是這個題卡hash函數,以後謹慎一點用雙hash... |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
void Smax(ll &a,ll b){if(a<b) a=b;}
void Smin(ll &a,ll b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
char s[maxn];
int dp[5002][5002],ans[5005];
ll Has[maxn],p=2333333,Pow[maxn],Hass[maxn];
int judge(int l1,int r1,int l2,int r2){
ll t1=(Has[r1]-(Has[l1-1]*Pow[r1-l1+1]%mod)+mod)%mod;
ll t2=(Hass[l2]-(Hass[r2+1]*Pow[r2-l2+1]%mod)+mod)%mod;
return t1==t2;
}
int Sheryang()
{
scanf("%s",s+1);
int n=strlen(s+1);
Pow[0]=1;
for(int i=1;i<=n;i++){
dp[i][i]=1;
Has[i]=(Has[i-1]*p+s[i])%mod;
Pow[i]=(Pow[i-1]*p)%mod;
}
for(int i=n;i>=1;i--){
Hass[i]=(Hass[i+1]*p+s[i])%mod;
}
ans[1]=n;
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
if(judge(i,i+len/2-1,j-len/2+1,j)){
dp[i][j]=min(dp[i][i+len/2-1],dp[j-len/2+1][j])+1;
ans[dp[i][j]]++;
}
}
}
for(int i=n-1;i>=1;i--){
ans[i]+=ans[i+1];
}
for(int i=1;i<=n;i++){
printf("%d ",ans[i]);
}
return 0;
}
Misunderstood … Missing 2018 Ec-final 西安(二維01揹包)題意:初始玩傢俱有攻擊力A和攻擊力增幅D都爲0,每一回合有三種操作,1.直接對敵方造成A+a[i]點傷害,2.將攻擊力增幅提升b[i] 3.將攻擊力提升c[i] 回合結束時,攻擊力提升當前的攻擊力增幅,求n回合後對敵方造成的最大傷害。 題解:可以想到逆推,若最後一回合,那麼一定要選擇第一個操作,那麼若是第i回合,我們討論第二種操作和第三種操作對於答案的影響,對於第三種種操作,對於傷害會提升後面的攻擊次數*c[i],那麼我們dp狀態要加上後面的攻擊次數,第二種狀態,這個還要設計到後面攻擊發動的位置,那麼我們設後面j次攻擊的位置分別爲k1,k2...kj,設k=k1+k2+...+kj,那麼對於答案的影響是(k1-i+k2-i+...+kj-i)*b[i]=(k-j*i)*c[i],然後還要滾動數組優化一下,同時狀態的枚舉要參照揹包那樣,倒着枚舉。 |
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
#define TYPE ll
const int mod=1e9+7;
void Smax(TYPE &a,TYPE b){if(a<b) a=b;}
void Smin(TYPE &a,TYPE b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//ar buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<TYPE,TYPE>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll a[105],b[105],c[105],dp[2][102][5051];
int Sheryang(){
int T=read;
while(T--){
memset(dp,0,sizeof(dp));
int n=read;
for(int i=1;i<=n;i++){
a[i]=read;
b[i]=read;
c[i]=read;
}
dp[n&1][1][n]=a[n];
ll ans=0;
for(int i=n-1;i>=1;i--){
for(int j=n-i;j>=1;j--){
int up=(2*n-j+1)*j/2,down=(2*i+j-1)*j/2;
for(int k=up;k>=down;k--){
int now=i&1,pre=(i+1)&1;
Smax(dp[now][j+1][k+i],dp[pre][j][k]+a[i]); //第i次攻擊
Smax(dp[now][j][k],dp[pre][j][k]+j*c[i]);
Smax(dp[now][j][k],dp[pre][j][k]+(k-i*j)*b[i]);
Smax(ans,dp[now][j+1][k+i]);
Smax(ans,dp[now][j][k]);
}
}
}
printf("%lld\n",ans);
}
return 0;
}
Educational Codeforces Round 60 (Rated for Div. 2) D. Magic Gems(dp+矩陣快速冪優化)題意:有n個空間,有兩種數字1和0,他們分別都能佔據1個空間,初始你有任意個數字1,數字1可以轉化成m個數字0,求佔滿n個空間的方案數. 題解:狀態轉移方程其實挺好寫的dp[i]代表佔據i空間的方案數,dp[i]=dp[i-1]+dp[i-m]佔據i個空間可以是佔據i-1空間和佔據i-m空間枚舉過來的,當i<m時,dp[i]=1,但是n特別大1e18,所以考慮優化這個遞推式,可以用矩陣快速冪:初始矩陣便是m行1列,dpn]一直到dp[n-m+1], 然後根據上一項的寫轉移矩陣就可以了。 |
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
#define TYPE int
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
void Smax(TYPE &a,TYPE b){if(a<b) a=b;}
void Smin(TYPE &a,TYPE b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
struct mat{
ll m[105][105];
};
int m;
const mat operator *(mat &a,mat &b){
mat ans;
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
ans.m[i][j]=0;
for(int k=1;k<=m;k++){
ans.m[i][j]+=a.m[i][k]*b.m[k][j];
if(ans.m[i][j]>=mod) ans.m[i][j]%=mod;
}
}
}
return ans;
}
ll qpow(mat a,ll b){
mat ans;
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
ans.m[i][j]=(i==j);
}
}
while(b){
if(b&1) ans=ans*a;
a=(a*a);b>>=1;
}
ll res=ans.m[1][1];
for(int i=1;i<=m;i++){
res+=ans.m[1][i];
if(res>=mod) res-=mod;
}
return res;
}
int Sheryang(){
ll n=read;
m=read;
if(n<m){
return printf("1"),0;
}else if(n==m){
return printf("2"),0;
}
mat a;
memset(a.m,0,sizeof(a.m));
a.m[1][1]=1;a.m[1][m]=1;
for(int i=2;i<=m;i++){
a.m[i][i-1]=1;
}
printf("%lld\n",qpow(a,n-m));
return 0;
}
Codeforces Round #548 (Div. 2) D.Steps to On(期望DP+莫比烏斯反演)題意:有一個爲空的隊列,每次往隊列中隨機添加1-m中的一個數,然後求出隊列的gcd,若gcd==1則終止,否則繼續添加,求最終的隊列期望長度。 題解:首先我們可以得到一個期望dp的式子來 但是這樣的複雜度是O() ,複雜度還是不足以過掉這道題,所以我們需要進行優化: 設代表中有多少個i使得 成立,則 如此,問題便變成了如何解決? 易知 如此我們便構造一個新函數 ,如此根據莫比烏斯反演的公式
代入即可,複雜度O。
|
---|
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
#define TYPE int
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
void Smax(TYPE &a,TYPE b){if(a<b) a=b;}
void Smin(TYPE &a,TYPE b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
vector<int>v[maxn];
int pri[maxn],vis[maxn],mu[maxn],n;
ll f[maxn];
void primeall(){
int cot=0;
for(int i=2;i<maxn;i++){
if(vis[i]==0){
pri[cot++]=i;
mu[i]=-1;
}
for(int j=0;j<cot&&i*pri[j]<maxn;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){
break;
}
mu[i*pri[j]]=-mu[i];
}
}
}
int calc(int x,int y){ /// 1-n gcd(x,i)==y
int g=x/y,ans=0;
for(auto p:v[g]){
ans+=mu[p]*(n/p/y);
}
return ans;
}
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;b/=2;
}
return ans;
}
int Sheryang(){
primeall();
n=read;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){
v[j].push_back(i);
}
}
f[1]=1;mu[1]=1;
ll inv=qpow(n,mod-2),ans=1;
for(int i=2;i<=n;i++){
for(int j=0;j<v[i].size()-1;j++){
int d=v[i][j];
f[i]=(f[i]+f[d]*calc(i,d)%mod)%mod;
}
f[i]=(f[i]*inv+1)%mod;
f[i]=f[i]*n%mod*qpow(n-calc(i,i),mod-2)%mod;
ans+=f[i];ans%=mod;
}
printf("%lld\n",ans*inv%mod);
return 0;
}
浙江師範大學校賽 G.Little Sub and Piggybank(動態規劃)
題意:有一個神奇的存錢罐,每天可以往裏面存任意實數的錢,但是後一天存的錢不能比前一天存的錢多,每天都有價值爲v的商品,只有當存錢罐裏的錢等於商品的價值纔可以購買,購買後將得到快樂值,求最大的快樂值。
題解:可以設計dp[i][j]代表最後買的商品是i倒數第二件買的商品是j所能得到的最大快樂值,那麼狀態轉移便是dp[i][j] = max(dp[j][k]) + v[i]; 如何直接轉移複雜度是O(n^3) 但是這個max(dp[j][k])其實是可以在之前的轉移中去維護的,定義mx[i][j] = max(dp[i][j]~dp[i][i]) ,則狀態轉移便可以改寫成 dp[i][j] = mx[j][k] + v[i] k是任意的嗎?肯定不是的,我們便要找到最小的k,又因爲之前的條件,後一天存的錢不能比前一天多, 於是化簡得到
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int w[maxn],v[maxn],dp[2005][2005],mx[2005][2005];
int Sheryang(){
int n=read;
for(int i=1;i<=n;i++){
w[i] = read;
}
for(int i=1;i<=n;i++){
v[i] = read;
}
// mx[i][j] dp[i][j] - dp[i][i-1]的最大值
// dp[i][j] = max(dp[j][k]) + v[i]
for(int i=1;i<=n;i++){
dp[i][0] = v[i];
for(int j=1;j<i;j++){
int k = (w[i]==0)? 0:max((int)ceil( j*1.0-1.0*w[j]/w[i]*(i-j) ),0);
if(mx[j][k]) dp[i][j] = mx[j][k] + v[i];
}
for(int j=i-1;j>=0;j--){
mx[i][j] = max(mx[i][j+1],dp[i][j]);
}
}
int ans = 0;
for(int i=1;i<=n;i++){
ans = max(ans,mx[i][0]);
}
printf("%d\n",ans);
return 0;
}
山東省第十屆省賽 B.Flipping Game(DP)
題意:給你長度爲n的01串的初始狀態和最終狀態,可改變m輪,每輪改變k個字符的狀態 0->1 1->0,輸出從初始狀態變到末尾狀態的方案數(n,m,j<=100).
題解:比賽的時候死活沒想到這個咋dp....狀態想不到,賽後看到別人代碼恍然大悟,其實是個蠻簡單的dp,代表第i輪s和t有j個相同字符的方案數,如此狀態轉移便可以枚舉之前的狀態,然後枚舉將多少個1變成0,然後組合數計算一下便可以了.....
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=5e4+7;
typedef long long ll;
const int mod=998244353;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll dp[105][105],C[105][105];
char s[105],t[105];
int Sheryang(){
C[0][0] = 1;
for(int i=0;i<=100;i++){
for(int j=0;j<=i;j++){
if(i==j || !j){
C[i][j] = 1;
}else{
C[i][j] = C[i-1][j] + C[i-1][j-1];
}
if(C[i][j]>=mod){
C[i][j] -= mod;
}
}
}
int T = read;
while(T--){
int n=read,m=read,K=read;
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
dp[i][j] = 0;
}
}
scanf("%s%s",s+1,t+1);
int num = 0;
for(int i=1;s[i];i++){
if(s[i] == t[i]){
num ++;
}
}
dp[0][num] = 1; //初始有num位相同
for(int i=0;i<m;i++){
for(int j=0;j<=n;j++){ //枚舉有多少個 0
if(dp[i][j]){
int limit = min(j,K);
for(int k=0;k<=limit;k++){ // k位相同的翻成不同的 K-k位不同翻成相同
dp[i+1][j+(K-k)-k] += dp[i][j]*C[j][k]%mod*C[n-j][K-k]%mod;
dp[i+1][j+(K-k)-k] %= mod;
}
}
}
}
printf("%lld\n",dp[m][n]);
}
return 0;
}
Educational Codeforces Round 64 (Rated for Div. 2)(換根dp)
題意:給你一顆n個節點的樹,邊權只有0和1,在樹上選擇兩個節點u,v,若u到v的邊權是不遞減的,則是合法的路徑,求一共有多少的合法路徑?
題解:dp[u][0/1/2/3/4]從u到底,全0 先0後1 先1後0 全1的路徑有多少,然後就可以枚舉一條邊進行換根dp,狀態轉移有點多,需要仔細考慮一下所有的可行狀態,同時若u->v路徑上的權值是不變的,那麼答案就要*2,同時注意一下狀態是從上到下的就可以了......
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
//#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
int nxt[maxn<<2],head[maxn],to[maxn<<2],w[maxn<<2],cnt;
void add(int u,int v,int vol){
nxt[++cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
w[cnt] = vol;
}
ll dp[maxn][5],Ans;
// 00 01 10 11
void dfs(int u,int fa){
ll tmp = 0;
for(int i=head[u];~i;i=nxt[i]){
int v = to[i];
int vol = w[i];
if(v == fa){
continue;
}
dfs(v,u);
if(vol){
tmp += dp[u][0]*(dp[v][3]+1);
tmp += dp[u][2]*(dp[v][3]+1);
tmp += dp[u][3]*(dp[v][0]+dp[v][2]+2*dp[v][3]+2);
tmp += dp[v][0]+dp[v][2]+2*dp[v][3]+2;
dp[u][3] += dp[v][3] + 1;
dp[u][2] += dp[v][0] + dp[v][2];
}else{
tmp += dp[u][0]*(2*dp[v][0] + dp[v][1] + dp[v][3] + 2);
tmp += dp[u][1]*(dp[v][0]+1);
tmp += dp[u][3]*(dp[v][0]+1);
tmp += 2*dp[v][0] + dp[v][1] + dp[v][3]+2;
dp[u][0] += dp[v][0]+1;
dp[u][1] += dp[v][1] + dp[v][3];
}
}
Ans += tmp;
}
int Sheryang(){
int n = read;
memset(head,-1,sizeof head);
for(int i=1;i<n;i++){
int u = read, v = read,vol = read;
add(u,v,vol);
add(v,u,vol);
}
dfs(1,0);
printf("%lld\n",Ans);
return 0;
}
Atcoder AGC 030D Inversion Sum(期望dp)(好題)
題意:有一個序列,有Q次操作,每次給出x和y,可以選擇是否交換x和y位置上的數字,共有種可能,計算所有可能的逆序數的和。
題解: 代表前i輪的期望次數,若這輪選擇交換 不交換的話 其他n個位置同理,最後乘上總方案數就可以了。
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll qpow(ll a,ll b){
ll ans = 1;
while(b){
if(b&1) ans = ans*a%mod;
a=a*a%mod; b/=2;
}
return ans;
}
ll dp[3005][3005];
int a[3005];
int Sheryang(){
int n = read , Q = read;
ll tot = qpow(2,Q);
ll inv = qpow(2,mod-2);
for(int i=1;i<=n;i++){
a[i] = read;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j] = (a[i]<a[j]);
}
}
// dp[x][y] a[x]<a[y] 的期望
while(Q--){
int x = read,y = read;
dp[x][y] = dp[y][x] = (dp[x][y]+dp[y][x])*inv%mod;
for(int i=1;i<=n;i++){
if(i!=x&&i!=y){
dp[i][x] = dp[i][y] = (dp[i][x]+dp[i][y])*inv%mod;
dp[x][i] = dp[y][i] = (dp[x][i]+dp[y][i])*inv%mod;
}
}
}
ll ans = 0;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
ans += dp[i][j];
if(ans>=mod) ans -= mod;
}
}
ans = ans*tot%mod;
printf("%lld\n",ans);
return 0;
}