luogu4187
P4187 [USACO18JAN]Stamp Painting
样例
input
3 2 2
output
6
input
6 10 5
output
190
sol:首先可以发现,对于合法的序列,只要有一串至少连续K个相同的就可以了,其他没有限制
这当然是可以dp辣
dp[i][j]表示前i位没有,当前有j个连续相同,前面没有出现连续K个相同
统计答案的时候就是∑i={K,n} dp[i][K]*Ksm(m,n-i)
转移挺容易的
dp[1][1]=m
dp[i][1]=(m-1)*∑j={1,K-1} dp[i-1][j]
dp[i][j=(2~K)] = dp[i-1][j-1]
但是就这样裸的暴力肯定是n2的
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const ll Mod=1000000007; const int N=1005; int n,m,K; ll dp[N][N]; //dp[i]表示前i位没有,当前有j个连续相同,前面没有出现连续K个相同 inline void Ad(ll &x,ll y) { x+=y; x-=(x>=Mod)?Mod:0; } inline ll Ksm(ll x,ll y) { ll ans=1; while(y) { if(y&1) ans=ans*x%Mod; x=x*x%Mod; y>>=1; } return ans; } int main() { freopen("data.in","r",stdin); freopen("baoli.out","w",stdout); int i,j; ll ans=0; R(n); R(m); R(K); dp[1][1]=m; for(i=2;i<=n;i++) { for(j=1;j<=(K-1)&&j<=(i-1);j++) Ad(dp[i][1],dp[i-1][j]*(m-1)%Mod); for(j=2;j<=K&&j<=i;j++) { dp[i][j]=dp[i-1][j-1]; } } // for(i=1;i<=n;i++) // { // for(j=1;j<=K;j++) W(dp[i][j]); // puts(""); // } for(i=K;i<=n;i++) Ad(ans,dp[i][K]*Ksm(m,n-i)%Mod); Wl(ans); return 0; } /* input 3 2 2 output 6 */
然后面临的问题就是怎么优化这个dp,容易发现其实每次除了第一位,另外都是不变的(向右移一位而已),所以只要更新第一位的值可以了
有这样两个队列
1 2 3 4 5 Head=10 Tail=14
x 1 2 3 4 Head=9 Tail=13
发现了吗,只要搞一个队列,每次Head-1,Tail-1就会向左移一位,那个红色的x就是要更新的值了
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const ll Mod=1000000007; const int N=1000005; int n,m,K; ll dp[N]; //dp[j]表示前i位(已经滚掉)没有,当前有j个连续相同,前面没有出现连续K个相同 ll Queue[N<<1]; ll Bin[N]; inline void Ad(ll &x,ll y) { x+=y; x-=(x>=Mod)?Mod:0; x+=(x<0)?Mod:0; } int main() { freopen("data.in","r",stdin); freopen("my.out","w",stdout); int i; ll Sum=0,ans=0; R(n); R(m); R(K); dp[1]=m; for(i=2;i<=K;i++) dp[i]=0; Sum=m; for(i=1;i<=K;i++) Queue[n+i]=dp[i]; Bin[0]=1; for(i=1;i<=n;i++) Bin[i]=Bin[i-1]*m%Mod; int Head=n+1,Tail=n+K; Ad(ans,Queue[Tail]*Bin[n-1]%Mod); for(i=2;i<=n;i++) { Queue[Head-1]=(Sum-Queue[Tail]+Mod)*(m-1)%Mod; Ad(Sum,Queue[Head-1]); Ad(Sum,(-1)*Queue[Tail]); Head--; Tail--; // for(int j=Head;j<=Tail;j++) W(Queue[j]); // puts(""); Ad(ans,Queue[Tail]*Bin[n-i]%Mod); } Wl(ans); return 0; } /* input 3 2 2 output 6 input 6 10 5 output 190 */ /* 1 2 3 4 5 Head=10 Tail=14 1 2 3 4 Head=9 Tail=13 */
Ps:最后附上对拍
:loop make.exe luogu4187.exe baoli.exe fc my.out baoli.out if not errorlevel 1 goto loop pause goto loop
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int Mod=1000; int main() { freopen("data.in","w",stdout); srand(time(NULL)); int n,m,k; n=rand()%Mod+1; m=rand()%Mod+1; k=rand()%n+1; W(n); W(m); Wl(k); return 0; }
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!