1. 程式人生 > >codeforces round#524 D - Olya and magical square /// 大概算是數學規律題?

codeforces round#524 D - Olya and magical square /// 大概算是數學規律題?

題目大意:

t 個測試用例  (1t103)

給定n k  (1n10^9,1k10^18)

表示有一個邊長為2^n的正方形格子 每次操作只能將一個格子切割為左上左下右上右下的四等分格子

問進行k次四等分切割後 能否使得 左下角的格子的邊長 和 右上角的格子的邊長 相等

並且存在一條左下角格子到右上角格子的路徑上 經過的格子的邊長 也和它們相等

若可以輸出 “YES 切割後的log2(邊長)” 若操作次數用不完輸出“NO”

 

首先

對邊長為2^1的格子四等分切割為1*1小格需要         1          次操作

對邊長為2^2的格子四等分切割為1*1小格需要   1*4+1=5   次操作

對邊長為2^3的格子四等分切割為1*1小格需要  5*4+1=21  次操作

......op[ i ] = op[ i-1 ] * 4 + 1

由此可預處理出邊長為 2^i 的格子切割為1*1小格 需要 op[i] 次操作

 

然後我們可以發現當切割邊長為2^31的格子時 op[31] 即操作次數超出了k的範圍10^18

(k最大時不足以將一個2^31邊長的格子切為1*1小格)

假設 邊長為2^32時 先進行一次操作(分為四格2^31) 剩k-1次操作

之後只對右下的2^31的一格切割 那麼k-1次操作絕對能用完

所以路徑由左下經左上到右上 經過的三格的邊長一樣都是2^31 即輸出log2(2^31)=31

以此類推 >31 的情況 只要這麼處理 答案就是 n-1 

 

n>=31時 ​考慮只切我們要走的路徑的格子(假設我們走左邊和上邊的邊緣圈的格子)

每次只對邊緣圈的格子切割一次

第一次需要切割 1 格 (即切1次) (路徑格子邊長減為 2^(n-1) )

第二次需要切割 3 格 (即切3次) (路徑格子邊長減為 2^(n-2) ) 

第三次需要切割 7 格 (即切7次) (路徑格子邊長減為 2^(n-3) )

......

每次遞推可得到下次需要切割的格子數 now(下次) = now(本次) * 2 + 1

累加得到邊緣圈應切割次數 tot += now

 

但是僅僅只切割外圍 k次操作很可能還是用不完的

那麼此時我們考慮每次切割後不會成為外圍圈的格子 

因為它們不會影響到我們要走的路徑 所以可以直接把它們切成1*1的小格

 

第一次紅色格子可切割 共需切割次數 op[ n-1 ] * (3-2)

第二次綠色格子可切割 共需切割次數 op[ n-2 ] * (7-2)

第三次青色格子可切割 共需切割次數 op[ n-3 ] * (15-2)

.....(由於恰好對應下次切割要切割的外圍圈格子往內的一圈 往內一圈會少兩格 所以恰好是 now(下次)-2 格)

累加得到額外可切割次數 re

 

那麼當只切外圍圈的運算元 tot >= k 時 可得到答案

或者 當切外圍也切內圈 tot+re>=k 時 也可得到答案

否則 k次操作 就不可能被用完

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k,op[100];
int main(){
    for(int i=1;i<=31;i++) op[i]=op[i-1]*4LL+1LL;
    int t; scanf("%d",&t);
    while(t--){
        scanf("%I64d%I64d",&n,&k);
        if(n>31) {
            printf("YES %I64d\n",n-1);
            continue;
        }
        ll tot=0,now=1,j=0,re=0;
        while(now+tot<=k&&j<n){  
            tot+=now;  
            now=now*2+1; // now更新為下輪操作需要操作的邊緣圈的格數
            j++; // 對邊緣圈的小格各操作一次 那麼每格的邊長又小了一半 即由2^(n-j)變為2^(n-(j+1))
            re+=op[n-j]*(now-2);  
        }
        if(k>tot+re) printf("NO\n"); // 全部切到1*1小格的操作次數tot+re 仍然不夠k次 
        else printf("YES %I64d\n",n-j); // n-j 即縮小到最後的 log2(邊長)
    }
    return 0;
}
View Code