首页 > 技术文章 > luogu4187

gaojunonly1 2019-04-11 22:16 原文

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
*/
View Code

 

然后面临的问题就是怎么优化这个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
*/
View Code

 

 

Ps:最后附上对拍

:loop
make.exe
luogu4187.exe
baoli.exe
fc my.out baoli.out
if not errorlevel 1 goto loop
pause
goto loop
pai
#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;
}
make

 

推荐阅读