UVA 1101 To Add or to Multiply

    首先咱們觀察加操做和乘操做會對區間產生那些影響。加操做只會平移區間,而乘操做既能移動區間還能放大區間。所以咱們不難想到,若是m>1的話乘操做是log級別的,一方面是由於區間的大小不能超過s-r,另外一方面區間的最大值不能超過r,這兩方面都能決定乘操做的個數是log級別的,所以一種可行的思路就是枚舉乘操做的次數,而後再看怎麼安排加操做。spa

    同時咱們還能發現,這些操做不會改變區間內數的順序,所以只要最小值和最大值都在s到r之間就能夠,而且若是咱們固定了乘操做的次數,至關於固定了最終區間的寬度,這樣根據最小值就能推出最大值,所以咱們能夠只約束最小值在某個範圍內,就能使得最小值和最大值都在s到r之間,這樣咱們又進一步簡化了問題。code

    如今的問題就是在乘操做固定的狀況下,要怎麼安排加操做才能使最小值(也就是p)落在這個範圍(指前面所說的「咱們能夠只約束最小值在某個範圍內」的這個範圍)內。blog

    咱們會發現不論加操做和乘操做的順序如何,最後p變成的數都能表示成x*p+y的形式,並且x是m的整數次冪,y是m的一些整數次冪和a的乘積,並且若是把y中的那個公因子的a提出來,就會發現剩下的部分就是m的一些整數次冪的和,並且這些整數次冪的係數之和就是加法的次數。數學

    用數學一點的形式表示上面加粗的部分就是說y=x0 * m^0 + x1* m^1 + x2 * m^2 + ...,其中x0 + x1 + x2 ...獲得的值就是加法的次數,而且x0這個部分表示的是最後才加的那些a,x1這個部分表示的是在進行最後一次乘m的操做前最後加的那些a,x2這個部分表示……string

    接下來咱們能夠先求一個不小於r的一個最小的x*p+y,由於x已經知道了,是m的某次冪,在乘操做次數固定後就是定值了,並且y必定是a的整數倍,那麼結合模運算就不難求得y的值了,這樣就獲得了不小於r的一個最小的x*p+y,若是這個值都不在前面所說的那個範圍的話,那麼這種乘操做次數下就必定無解了。it

    那麼若是這個值在所說的那個範圍內呢?那麼必定會有一個可行解。還會不會有其餘可行解呢?是有可能的,由於畢竟咱們只求了一個最小的x*p+y,但什麼狀況下才有可能更新最優解呢?至少也得比咱們剛剛算的這個可行解更優吧,可是乘法次數必定固定了呀,那麼咱們就考慮讓加法次數更少一些。而咱們剛剛說了y=x0 * m^0 + x1* m^1 + x2 * m^2 + ...,這個實際上不就是一個m進制數麼,若是想讓各個位上的數字之和更小的話就必需要產生進位,這樣纔可能消掉一部分,但卻只付出增長一個進位的代價,這個進位要麼只使各位數字之和增長1,要麼還能進一步減小各位數字之和的值。因此咱們就能夠從低位到高位依次考慮可不能夠進位了,那麼就先考慮能不能經過增長m-x0消掉x0,若是能夠的話咱們再考慮能不能在原有的基礎上再增長(m-x1)*m從而消掉x1,若是能夠的話再考慮……就這樣一直消到不能消爲止,期間每消一次就更新一次最優解。io

    前面在討論什麼狀況下有可能更新最優解的時候其實少了一點沒有考慮,就是操做數是同樣的,可是可能字典序會更小。不過這點實際上是不用考慮的,由於若是存在這種狀況,那麼必定是先產生進位以後才能再出現這樣的狀況,而剛產生進位的時候就要麼是操做數同樣,要麼是操做數更小了,因此就不用再考慮這種狀況了,只須要考慮剛進位的時候就好了。class

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define INF 0x3f3f3f3f
#define MAXN 110
typedef long long LL;
int A, M, P, Q, R, S;
struct List
{
    int s, n, cnt[MAXN];
    bool add[MAXN];
    bool operator < (const List &t) const
    {
        if(s != t.s) return s < t.s;
        int p = 0, r = cnt[0], tp = 0, tr = t.cnt[0];
        while(p < n && tp < t.n)
        {
            if(add[p] && !t.add[tp]) return true;
            if(!add[p] && t.add[tp]) return false;
            int min = std::min(r, tr);
            r -= min, tr -= min;
            if(r == 0) r = cnt[++ p];
            if(tr == 0) tr = t.cnt[++ tp];
        }
        return p == n && tp < t.n;
    }
}ans;
List gen(int a[], int mn)
{
    List ret;
    ret.s = mn, ret.n = 0;
    for(int i = 0, &n = ret.n; i <= mn; i ++)
    {
        ret.s += a[i];
        if(a[i] > 0)
        {
            ret.cnt[n] = a[i], ret.add[n] = true;
            ++ n;
        }
        if(i < mn)
        {
            if(n == 0 || ret.add[n - 1])
            {
                ret.cnt[n] = 1, ret.add[n] = false;
                ++ n;
            }
            else ++ ret.cnt[n - 1];
        }
    }
    return ret;
}
void process(int mn, int p, int px, int py)
{
    if(p > px) px = p;
    LL sum = ((p - px) % A + A) % A + px;
    if(sum > py) return;
    sum = (sum - p) / A;
    int t = sum, max = (py - p) / A, a[MAXN];
    for(int i = mn; i > 0; i --) a[i] = t % M, t /= M;
    a[0] = t;

    List opt;
    for(int i = mn, fac = 1; i >= 0; i --, fac *= M)
    {
        opt = gen(a, mn);
        if(opt < ans) ans = opt;
        if(i > 0 && a[i] != 0)
        {
            sum += (LL)(M - a[i]) * fac, a[i] = M;
            if(sum > max) break;
            for(int j = i; j > 0 && a[j] == M; j --)
                ++ a[j - 1], a[j] = 0;
        }
    }
}
int main()
{
    int t = 0;
    while(scanf("%d%d%d%d%d%d", &A, &M, &P, &Q, &R, &S), A > 0)
    {
        ans.s = INF;
        LL len = Q - P, p = P;
        for(int i = 0; p <= S - len && len <= S - R; i ++, len *= M, p *= M)
        {
            process(i, p, R, S - len);
            if(M == 1) break;
        }
        printf("Case %d:", ++ t);
        if(ans.s == INF) printf(" impossible");
        else if(ans.s == 0) printf(" empty");
        else
        {
            for(int i = 0; i < ans.n; i ++)
                printf(" %d%c", ans.cnt[i], ans.add[i] ? 'A' : 'M');
        }
        printf("\n");
    }
    return 0;
}
相關文章
相關標籤/搜索