Editorial for Bedao OI Contest 6 - Tiệm bánh VNOI


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.

Subtask 1:

Đặt hàm ~DP[i]~ là tổng của tích của các dãy có ~F(A) = i~. Ta có thể viết công thức quy hoạch động là: ~DP[i] = \sum_{j=1}^{i-1} DP[j] \cdot (i - j)~ ~(1)~ , khi mà thêm phần tử ~j~ vào cuối các dãy có ~F(A) = i~ sẽ nhân giá trị tích lên ~j~.

Nếu đặt ~D[i]~ là tổng tiền tố của hàm ~DP[i]~ sao cho: ~D[i] = DP[1] + DP[2] + \ldots + DP[i] = \sum_{j=1}^{i} DP[j]~. Thì ~(1)~ có thể suy diễn thành: ~DP[i] = D[1] + D[2] + \ldots + D[i-1]~

Đặt thêm hàm ~C[i]~ là tổng tiền tố của hàm ~D[i]~, ta có thể viết lại công thức như sau:

  • ~DP[i] = C[i-1]~

  • ~D[i] = D[i-1] + DP[i]~

  • ~C[i] = C[i-1] + D[i]~

Từ đó ta chỉ cần một vòng lặp để duyệt qua các chỉ số ~i~ và tính lần lượt các giá trị ~DP[i], D[i], C[i]~.

Độ phức tạp: ~O(R)~.

Subtask 2:

Ở đây ta cần áp dụng thêm điều kiện các phần tử trong tập hợp phải chia hết cho ít nhất một phần tử thuộc ~K~. Đặt ~S = LCM(K)~.

Ở đây ta viết lại công thức ~(1)~ như sau

  • ~DP[i] = \sum_{j=1}^i DP[i-j] \times j, \quad \forall j(\exists x, j | K_x)~

Ý tưởng ở đây là chia tổng tiền tố ~C[i], D[i]~ thành các thành phần đồng dư riêng biệt ~C[i][k], D[i][k], 0 \leq k < S~ để tính tổng các ~DP[j]~ sao cho ~(i-j) \equiv k \pmod{S}~.

Ta định nghĩa lại 2 hàm sau:

  • ~D[i][k]~ là tổng các tiền tố ~DP[j]~ thỏa ~(i-j) \equiv k \pmod{S}~

  • ~C[i][k]~ là tổng các tiền tố ~D[j][k]~, là tổng các ~DP[j]~ nhân hệ số của nó là ~(i-j+1)~ thỏa ~(i-j) \equiv k \pmod{S}~

Viết lại công thức quy hoạch động như sau:

  • ~DP[i] = \sum_{u=0, (u+1)|K}^{S-1} C[i-1][u]~, thỏa điều kiện ~u+1~ chia hết cho một phần tử thuộc tập ~K~.

  • ~D[i][0] = D[i-1][S-1] + DP[i]~

  • ~C[i][0] = C[i-1][S-1] + D[i][0]~

  • ~D[i][k] = D[i-1][k-1], \forall k=1..S-1~

  • ~C[i][k] = C[i-1][k-1] + D[i][k], \forall k=1..S-1~

Chuyển từ ~i-1~ đến ~i~ thì giá trị đồng dư ~k~ cũng phải tăng lên 1, nên ~DP[i][k]~, ~C[i][k]~ chuyển trạng thái từ ~D[i-1][k-1]~, ~C[i-1][k-1]~.

Ta duyệt hết index ~i~ để tính mọi giá trị ~DP[i]~.

Độ phức tạp: ~O(R)~.

Subtask 3:

Nhắc lại công thức ở subtask 1:

  • ~DP[i] = C[i-1]~

  • ~D[i] = D[i-1] + DP[i]~

  • ~C[i] = C[i-1] + D[i]~

Để viết hàm nhân ma trận, ta biến đổi một chút công thức truy hồi như sau:

  • ~D[i] = D[i-1] + DP[i]~

  • ~C[i] = C[i-1] + D[i-1] + DP[i]~

  • ~DP[i+1] = C[i] = C[i-1] + D[i-1] + DP[i]~

Đổi index ở ~DP[i+1]~ để viết ma trận cho dễ

$$\left(\begin{array}{ccc} 1 & 1 & 1 \\ 1 & 1 & 0 \\ 1 & 1 & 1 \end{array} \right) \times \left(\begin{array}{c} DP[i] \\ D[i-1] \\ C[i-1] \end{array} \right) = \left(\begin{array}{c} DP[i+1] \\ D[i] \\ C[i] \end{array} \right)$$

Độ phức tạp: ~O(\log_2{R})~.

Subtask 4:

Hàm ma trận ở đây đã tăng kích thước lên thành ~420 \times 420~ theo công thức tổng quát ở subtask 2.

Ở subtask 2:

  • ~DP[i] = \sum_{u=0, (u+1)|K}^{S-1} C[i-1][u]~ thỏa điều kiện ~u+1~ chia hết cho một phần tử thuộc tập ~K~

  • ~D[i][0] = D[i-1][S-1] + DP[i]~

  • ~C[i][0] = C[i-1][S-1] + D[i][0]~

  • ~D[i][k] = D[i-1][k-1], \forall k = 1..S-1~

  • ~C[i][k] = C[i-1][k-1] + D[i][k], \forall k = 1..S-1~

  • ~S[i] = S[i-1] + \sum_{u=0, (u+1)|K}^{S-1} C[i-1][u]~

Viết lại công thức như sau:

  • ~DP[i] = \sum_{u=0, (u+1) | K}^{S-1} C[i-1][u],~ thỏa điều kiện ~u+1~ chia hết cho một phần tử thuộc tập ~K~

  • ~D[i][0] = D[i-1][S-1] + \sum_{u=0, (u+1) | K}^{S-1} C[i-1][u]~

  • ~C[i][0] = C[i-1][S-1] + D[i-1][S-1] + \sum_{u=0, (u+1) | K}^{S-1} C[i-1][u]~

  • ~D[i][k] = D[i-1][k-1], \quad \forall k = 1, \ldots, S-1~

  • ~C[i][k] = C[i-1][k-1] + D[i-1][k-1], \quad \forall k = 1, \ldots, S-1~

  • ~S[i] = S[i-1] + \sum_{u=0, (u+1) | K}^{S-1} C[i-1][u]~

$$X \times \left(\begin{array}{c} S[i-1] \\ DP[i-1] \\ \\ D[i-1][0] \\ C[i-1][0] \\ D[i-1][1] \\ C[i-1][1] \\ \vdots \\ D[i-1][79] \\ C[i-1][79] \\ \end{array} \right) = \left( \begin{array}{c} S[i] \\ DP[i] \\ \\ D[i][0] \\ C[i][0] \\ D[i][1] \\ C[i][1] \\ \vdots \\ D[i][79] \\ C[i][79] \\ \end{array} \right)$$

Lưu ý:

  • Cần phải nháp và code tay khá kĩ cho ma trận

  • Có thể đổi sang ~S[i-2] = S[i-1] + DP[i-1]~ để bớt đi một nhân tử ~DP[i]~

Độ phức tạp: ~O([LCM(K_1, \ldots ,K_m)]^3 \times \log_2{R})~

#include<bits/stdc++.h>
#define endl '\n'
#define fi first
#define se second
using namespace std;
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ld,ld> ldld;
typedef pair<ll,ll> llll;

const int MOD=1e9+7;

ll l,r;
int n,s=1;
int k[121];

namespace sub1
{
    bool ok()
    {
        return s==1&&r<=1000000;
    }
    const int N=1e6+1;
    ll dp[N],d[N],c[N];
    void solve()
    {
        c[0]=1;
        for(int i=1;i<=r;i++)
        {
            dp[i]=c[i-1];
            d[i]=(d[i-1]+dp[i])%MOD;
            c[i]=(c[i-1]+d[i])%MOD;
        }
        cout << (c[r]-c[l-1]+MOD)%MOD << endl;
    }
}

namespace sub2
{
    bool ok()
    {
        return r<=1000000;
    }
    const int N=1e6+1;
    const int S=121;
    ll dp[2],d[2][S],c[2][S];
    void solve()
    {
        memset(dp,0,sizeof(dp));
        memset(d,0,sizeof(d));
        memset(c,0,sizeof(c));
        vector<int> vu;
        for(int i=1;i<=n;i++) for(int j=k[i];j<=s;j+=k[i]) vu.push_back(j);
        sort(vu.begin(),vu.end());
        vu.erase(unique(vu.begin(),vu.end()),vu.end());
        c[0][0]=d[0][0]=1;
        ll ans=0;
        for(int i=1;i<=r;i++)
        {
            int cc=(i&1);
            dp[cc]=0;
            for(int u:vu) (dp[cc]+=c[cc^1][u-1])%=MOD;
            d[cc][0]=(d[cc^1][s-1]+dp[cc])%MOD;
            c[cc][0]=(c[cc^1][s-1]+d[cc][0])%MOD;
            for(int k=1;k<s;k++)
            {
                d[cc][k]=d[cc^1][k-1];
                c[cc][k]=(c[cc^1][k-1]+d[cc][k])%MOD;
            }
            if(i>=l) (ans+=dp[cc])%=MOD;
        }
        cout << ans << endl;
    }
}

namespace sub3
{
    bool ok()
    {
        return s==1;
    }
    vector<vector<ll>> mul(vector<vector<ll>> &a,vector<vector<ll>> &b)
    {
        vector<vector<ll>> c(a.size(),vector<ll>(b[0].size(),0));
        for(int i=0;i<(int)a.size();i++) for(int j=0;j<(int)b[0].size();j++) for(int k=0;k<(int)b.size();k++) (c[i][j]+=a[i][k]*b[k][j]%MOD)%=MOD;
        return c;
    }
    vector<vector<ll>> base,dp;
    ll calc(ll x)
    {
        base.clear();dp.clear();
        base.resize(3);dp.resize(3);
        base[0]={1,1,1};
        base[1]={1,1,0};
        base[2]={1,1,1};
        dp[0]={1};
        dp[1]={0};
        dp[2]={1};
        while(x)
        {
            if(x&1) dp=mul(base,dp);
            base=mul(base,base);
            x>>=1;
        }
        return dp[0][0];
    }
    void solve()
    {
        cout << (calc(r)-calc(l-1)+MOD)%MOD << endl;
    }
}

namespace sub4
{
    vector<vector<ll>> mul(vector<vector<ll>> &a,vector<vector<ll>> &b)
    {
        vector<vector<ll>> c(a.size(),vector<ll>(b[0].size(),0));
        for(int i=0;i<(int)a.size();i++) for(int j=0;j<(int)b[0].size();j++) for(int k=0;k<(int)b.size();k++) (c[i][j]+=a[i][k]*b[k][j])%=MOD;
        return c;
    }
    vector<int> vu;
    vector<vector<ll>> base,dpr,dpl;
    ll calc(ll l,ll r)
    {
        /**
        s[i-1]
        dp[i-1]
        d[i-1][0]
        c[i-1][0]
        d[i-1][1]
        c[i-1][1]
        ...
        d[i-1][119]
        c[i-1][119]
        **/
        /// base
        base.clear();dpl.clear();dpr.clear();
        base.resize(2*s+2);dpr.resize(2*s+2);
        for(int i=0;i<2*s+2;i++) base[i].resize(2*s+2,0);

        base[0][0]=1;
        base[2][2*s]++;
        base[3][2*s]++;base[3][2*s+1]++;
        for(int u:vu)
        {
            base[0][2*u+1]++;
            base[1][2*u+1]++;
            base[2][2*u+1]++;
            base[3][2*u+1]++;
        }
        for(int i=1;i<=s-1;i++)
        {
            base[2+2*i][2*i]++;
            base[2+2*i+1][2*i]++;
            base[2+2*i+1][2*i+1]++;
        }
        /// dp
        dpr[0]={0};
        dpr[1]={0};
        dpr[2]={1};
        dpr[3]={1};
        for(int i=4;i/2<=s;i++) dpr[i]={0};
        dpl=dpr;
//        for(int i=0;i<2*s+2;i++)
//        {
//            for(int j=0;j<2*s+2;j++) cout << base[i][j] << ' ';
//            cout << " | " << dp[i][0] << endl;
//        }
        l--;
        while(r)
        {
            if(l&1) dpl=mul(base,dpl);
            if(r&1) dpr=mul(base,dpr);
            base=mul(base,base);
            r>>=1;l>>=1;
        }
//        cout << dp[0][0] << ' ' << dp[1][0] << endl;
        return (dpr[0][0]-dpl[0][0]+MOD)%MOD;
    }
    void solve()
    {
        vu.clear();
        for(int i=1;i<=n;i++) for(int j=k[i];j<=s;j+=k[i]) vu.push_back(j);
        sort(vu.begin(),vu.end());
        vu.erase(unique(vu.begin(),vu.end()),vu.end());
        cout << calc(l,r) << endl;
//        cout << (calc(r)-calc(l-1)+MOD)%MOD << endl;
    }
}

void solve()
{
    /// Input
    cin >> l >> r >> n;
    s=1;
    for(int i=1;i<=n;i++)
    {
        cin >> k[i];
        s=s/__gcd(s,k[i])*k[i];
    }
    /// Solve
    if(sub1::ok()) sub1::solve();
    else if(sub2::ok()) sub2::solve();
    else if(sub3::ok()) sub3::solve();
    else sub4::solve();
}

int main()
{
    freopen("a.inp","r",stdin);
    freopen("a.out","w",stdout);
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin >> t;
    while(t--)
    solve();
    return 0;
}

Comments

Please read the guidelines before commenting.


There are no comments at the moment.