7.5 模擬賽
阿新 • • 發佈:2021-07-05
7.5 模擬賽
DP專題
A 塗色paint
明顯的區間DP
轉移方程:
\[f[i][j]=\begin{cases} max(f[i+1][j],f[i][j-1])\;(c[i]==c[j])\\ max(f[i][k]+f[k+1][j])\;(c[i]!=c[j]) \end{cases} \]碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cmath> #include<vector> #include<map> #include<queue> #include<deque> #include<set> #include<stack> #include<bitset> #include<cstring> #define ll long long using namespace std; const int INF=0x3f3f3f3f,N=100010; inline int read(){ int x=0,y=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();} while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*y; } int n,w[N],s[N]; int t,h,q[N]; int f[N],g[N]; int main(){ n=read(); for(int i=n;i>=1;i--) w[i]=read(); for(int i=1;i<=n;i++) s[i]=s[i-1]+w[i]; h=0,t=0; for(int i=1;i<=n;i++){ while(h<t&&s[q[h+1]]+g[q[h+1]]<=s[i]) ++h; f[i]=f[q[h]]+1; g[i]=s[i]-s[q[h]]; while(h<t&&s[q[t]]+g[q[t]]>=s[i]+g[i]) --t; q[++t]=i; } printf("%d\n",f[n]); return 0; }
B 粉刷匠
預處理+分組揹包
預處理出每個木板塗k次最多能塗對多少塊,線性DP
用分組揹包,每個木板為一組,每個木板塗多少次是1個單位
選就完了
碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cmath> #include<vector> #include<map> #include<queue> #include<deque> #include<set> #include<stack> #include<bitset> #include<cstring> #define ll long long using namespace std; const int INF=0x3f3f3f3f; int n,m,t,mapp[55][2510]; int g[55][2510][55],f[2][2510]; inline int read(){ int x=0,y=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();} while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*y; } int main(){ n=read(),m=read(),t=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ mapp[i][0]=0; char a;cin>>a; mapp[i][j]=mapp[i][j-1]+(a=='1'); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=1;k<=m;k++) for(int q=j-1;q<k;q++) g[i][j][k]=max(g[i][j][k],g[i][j-1][q]+max(mapp[i][k]-mapp[i][q],k-q-mapp[i][k]+mapp[i][q]));//塗0還是塗1 for(int i=1;i<=n;i++){ for(int j=1;j<=t;j++){ for(int k=0;k<=min(m,j);k++){ int op=(i%2)^1; f[i&1][j]=max(f[i&1][j],f[op][j-k]+g[i][k][m]); } } } int ans=0; for(int i=1;i<=t;i++) ans=max(ans,f[n&1][i]); printf("%d\n",ans); return 0; }
C 花神的數論題
數位DP板子無壓力
甚至不用記錄前導零和原數
碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cmath> #include<vector> #include<map> #include<queue> #include<deque> #include<set> #include<stack> #include<bitset> #include<cstring> #define ll long long using namespace std; const ll INF=0x3f3f3f3f,Mod=10000007; ll n,len,ans=1,a[55]; ll f[55][55]; inline ll read(){ ll x=0,y=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();} while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*y; } ll dfs(int pos,int sum1,int lim){ if(pos>len){ if(sum1!=0) return sum1%Mod; return 1; } if(!lim&&f[pos][sum1]!=-1) return f[pos][sum1]%Mod; ll res=1; int ret=lim?a[len-pos+1]:1; for(int i=0;i<=ret;i++){ (res*=dfs(pos+1,sum1+(i==1),(lim&&i==ret)))%=Mod; } if(!lim) f[pos][sum1]=res; return res; } ll part(ll x){ len=0; while(x){a[++len]=x&1,x>>=1;} memset(f,-1,sizeof f); ll res=dfs(1,0,1); return res; } int main(){ n=read(); printf("%lld\n",part(n)); return 0; }
D 合法序列
注意到該題的k很小,最大也只能到4,所以我們可以列舉這k位,而這k位對整個序列合法性判斷上的影響也只有16位,也可以直接列舉,最大列舉量為\(2^{2^4}\),即131072
我們每列舉到一個\(2^{2^k}\)的狀態,就判斷它是不是合法的,如果它不合法,那麼就捨去這種情況,如果合法,就繼續轉移
轉移方程方面,我們設f[i][S]為從第i位開始k位是狀態S,我們在枚舉出來的\(2^{2^k}\)的狀態必然合法,那麼我們把最後一個長度為k的串在f陣列中標記為1
接下來就把這個列舉的序列不斷向後移去轉移
轉移有點難
我們需要先把列舉的狀態轉移成上一個狀態,即取該狀態的後k-1位,然後左移一位,就是上一個狀態,最後一位取1時函式值與最後一位取0時函式值之和就是該狀態的函式值
用到了a或上b再異或上b就是把b是1的位在a中都設成0
碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f,N=510,Mod=998244353;
int n,k,res=0;
int g[N],f[N][N];
inline int read(){
int x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
int main(){
n=read(),k=read();
for(int i=0;i<(1<<k);i++){
for(int j=0;j<k;j++){
g[i]|=((i>>j)&1)*(1<<(k-j-1));
}
}//編號和狀態位置相反
for(int i=0;i<(1<<(1<<k));i++){
int flg=1;
for(int j=0;j<=(1<<k)-k;j++){
int op=((1<<k)-1)&(i>>j);//取該狀態的每個k位
op=g[op];
if(i&(1<<op)) continue;//合法嗎
flg=0;
break;
}
if(!flg)continue;
memset(f,0,sizeof(f));
f[(1<<k)-1][i>>((1<<k)-k)]=1;
for(int j=(1<<k);j<n;j++){//從16開始往後dp
for(int l=0;l<(1<<k);l++){//列舉狀態
if((i&(1<<g[l]))==0) continue;
int op=((l|(1<<(k-1)))^(1<<(k-1)))<<1;
//cout<<j<<" "<<l<<" "<<op<<endl;
f[j][l]=(f[j-1][op|1]+f[j-1][op])%Mod;
}
}
for(int j=0;j<(1<<k);j++) res=(res+f[n-1][j])%Mod;
}
printf("%d\n",res);
return 0;
}