【CSA49G】【XSY3315】jump DP

題目大意

  有一個數軸。yww 最開始在位置 \(0\)yww 總共要跳躍不少次。每次 yww 能夠往右跳 \(1\) 單位長度,或者跳到位置 \(1\)數組

  定義位置序列爲 yww 在每次跳躍以後所在的位置組成的序列(顯然不包括 \(0\))。dom

  有 \(k\) 個數是好的,分別爲 \(a_1,a_2,\ldots,a_k\)優化

  定義一個位置序列是好的當且僅當:spa

  • 全部好的數的出現次數之和爲 \(n\)
  • 序列中最後一個數是好的。
  • 對於每個長度爲 \(m\) 的子區間,區間內至少有一個好的數。

  定義兩個位置序列本質相同當且僅當:code

  • 這兩個位置序列的長度相同。
  • 不存在一個數 \(t\),知足其中一個序列的第 \(t\) 項是一個好的數,且另外一個序列的第 \(t\) 項不是一個好的數。

  求全部本質不一樣的好的位置序列的長度之和。get

  對 \({10}^9+7\) 取模。string

  \(k\leq 100,n,m\leq {10}^9\)it

題解

  記 \(b_i=a_i-a_{i-1}\)io

  考慮對於一個最終位置是好的點的位置序列,求出和這個序列本質相同的序列中,最終能到達那些點。function

  這樣就有一個 \(O(2^kn)\) 的DP了。

  若是你把這個作法寫出來,就會發現其實只有 \(O(k)\) 個狀態是有用的。

  這是爲何呢?

  對於一個位置集合 \(S\),考慮集合內最大的元素 \(x\),對於一個更小的 \(y\)\(y\in S\) 當且僅當 \(b_1\leq b_{x-y+1}\)\(b_{2\ldots y}=b_{x-y+2 \ldots x}\) 這樣對於每一個 \(x\)\(S\) 是惟一的。

  直接套個矩陣快速冪優化DP就 \(O(k^3\log n)\) 了。


  還能夠換一種方向思考。

  考慮對於一個好的序列,每次取一個最短的前綴,知足後面仍是合法的好的序列。

  若是一個前綴能被分紅幾個前綴拼在一塊兒,就不能取這個前綴了。

  處理出刪掉 \(b_1\) 以後的 ex_kmp 數組就能夠快速求出每一個前綴是否可選。

  這就是一個常係數齊次線性遞推。

  直接BM+倍增取模能夠作到 \(O(k^2+k\log k\log n)\)

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
#include<map>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
using std::map;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int K=110;
const ll p=1000000007;
int k,m,n;
template <typename T>
T operator +(T a,T b)
{
    return T(a.first+b.first,a.second+b.second);
}
template <typename T>
T operator *(T a,T b)
{
    return T(a.first*b.first,a.first*b.second+a.second*b.first);
}
struct mat
{
    pii a[K][K];
    mat()
    {
        memset(a,0,sizeof a);
    }
    pii *operator [](int x)
    {
        return a[x];
    }
};
mat operator *(mat a,mat b)
{
    mat c;
    for(int i=1;i<=k;i++)
        for(int j=1;j<=k;j++)
        {
            std::pair<__int128,__int128> s;
            for(int l=1;l<=k;l++)
            {
                s.first+=(ll)a[i][l].first*b[l][j].first;
                s.second+=(ll)a[i][l].first*b[l][j].second;
                s.second+=(ll)a[i][l].second*b[l][j].first;
            }
            c[i][j]=pii(s.first%p,s.second%p);
        }
    return c;
}
mat fp(mat a,ll b)
{
    mat s;
    for(int i=1;i<=k;i++)
        s[i][i]=pll(1,0);
    for(;b;b>>=1,a=a*a)
        if(b&1)
            s=s*a;
    return s;
}
int b[K][K];
int a[K];
mat c,d;
map<int,int> s;
int sum(ll l,ll r)
{
    return (l+r)*(r-l+1)/2%p;
}
int main()
{
    open("jump");
    scanf("%d%d%d",&k,&m,&n);
    k=min(k,n);
    for(int i=1;i<=k;i++)
        scanf("%d",&a[i]);
    for(int i=k;i>=2;i--)
        a[i]-=a[i-1];
    if(a[1]>m)
    {
        printf("0\n");
        return 0;
    }
    for(int i=2;i<=k;i++)
        if(a[i]>m)
            k=i;
    for(int i=1;i<=k;i++)
    {
        b[i][i]=1;
        for(int j=1;j<i;j++)
        {
            int flag=1;
            if(a[1]>a[i-j+1])
                flag=0;
            for(int l=2;flag&&l<=j;l++)
                if(a[l]!=a[i-j+l])
                    flag=0;
            b[i][j]=flag;
        }
    }
    for(int i=1;i<=k;i++)
    {
        int cnt=m-a[1]+1;
        ll _s=sum(a[1],m);
        s.clear();
        for(int j=min(i,k-1);j>=1;j--)
            if(b[i][j]&&!s[a[j+1]])
            {
                s[a[j+1]]=1;
                c[i][j+1]=pll(1,a[j+1]);
                if(a[j+1]>=a[1])
                {
                    cnt--;
                    _s=(_s-a[j+1])%p;
                }
            }
        c[i][1]=pll(cnt,_s);
    }
    d[1][1]=pll(m-a[1]+1,sum(a[1],m));
    c=fp(c,n-1);
    d=d*c;
//  for(int i=1;i<n;i++)
//      d=d*c;
    ll ans=0;
    for(int i=1;i<=k;i++)
        ans=(ans+d[1][i].second)%p;
    ans=(ans%p+p)%p;
    printf("%lld\n",ans);
    return 0;
}
相關文章
相關標籤/搜索