zoukankan      html  css  js  c++  java
  • BZOJ3598

    题意

    方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。
    现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。
    例如:10 进制下的位置在 12312 的人,合并石子的最少代价为:
    1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9
    即把所有的石子都合并在第三堆

    题解

    先思考一下如何求最少代价。由于最后要合并为一堆,所以就是求移到哪一堆代价最小。假设pre和suf数组代表前缀和和后缀和。
    若当前的目标为i,每当目标右移一格(i+1),对代价的贡献为pre[i] - suf[i + 1]。这个贡献是单调递增的,所以最小代价的位置为贡献刚好由负转正的位置。

    这下就有dp的目标了。枚举目标位置,求出最小代价为目标位置的数和代价和,最后累加答案即可。状态为当前位置p,贡献sdif,目标位置的数字d(用于判断是否是临界位置,防止重复计数)。

    细节详见代码。

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
    #define FI freopen(".//data_generator//in.txt","r",stdin)
    #define FO freopen("res.txt","w",stdout)
    #define pb push_back
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 3e5 + 10;
    const double eps = 1e-5;
    typedef pair<ll, ll> PII;
    
    ll f[40][8000][40];
    ll cnt[40][8000][40];
    int vis[40][8000][40];
    int di[20];
    int tar, tag, k;
    
    PII dfs(int p, int sdif, int d, int lmt) {
        if(!p) {
            
            if(sdif <= 0 && 2 * d + sdif > 0) return mp(1, 0);
            return mp(0, 0);
        }
        if(!lmt && vis[p][sdif + 4000][d] == tag) 
            return mp(cnt[p][sdif + 4000][d], f[p][sdif + 4000][d]);
        ll res = 0;
        ll ct = 0;
        int maxx = lmt ? di[p] : (k - 1);
        for(int i = 0; i <= maxx; i++) {
            if(p > tar) {
                auto v = dfs(p - 1, sdif + i, i, i == maxx && lmt);
                ct += v.first;
                res += v.second + v.first * abs(tar - p) * i;
            } else {
                if(p == tar) {
                    auto v = dfs(p - 1, sdif - i, i, i == maxx && lmt);
                    ct += v.first;
                    res += v.second + v.first * abs(tar - p) * i;
                } else {
                    auto v = dfs(p - 1, sdif - i, d, i == maxx && lmt);
                    ct += v.first;
                    res += v.second + v.first * abs(tar - p) * i;
                }
            }
        }
        if(!lmt) {
            vis[p][sdif + 4000][d] = tag;
            f[p][sdif + 4000][d] = res;
            cnt[p][sdif + 4000][d] = ct;
        }
        return mp(ct, res);
    }
    
    
    ll solve(ll x) {
        int tot = 0;
        ll ans = 0;
        while(x) {
            di[++tot] = x % k;
            x /= k;
        }
        for(tar = 1; tar <= tot; tar++) {
            tag++;
            ans += dfs(tot, 0, 0, 1).second;
        }
        return ans;
    }
    
    int main() {
        IOS;
        ll l, r;
        cin >> l >> r >> k;
        cout << solve(r) - solve(l - 1) << endl;
    }
    
  • 相关阅读:
    鼠标放在图片上出现提示
    NSIS调用dll
    IIS7 CMD命令
    NSIS检测
    NSIS修改文件夹访问权限
    NSIS——检测IIS是否安装及版本
    NSIS——检测SQL Server安装版本
    NSIS使用技巧集合
    提供修复界面的NSIS安装包
    NSIS MUI教程
  • 原文地址:https://www.cnblogs.com/limil/p/13537776.html
Copyright © 2011-2022 走看看