魔法陣——數論

魔法陣——數論



題目來源

洛谷P2119


題目描述

六十年一次的魔法戰爭就要開始了,大魔法師準備從附近的魔法場中汲取魔法能量。

大魔法師有m個魔法物品,編號分別爲1,2,…,m。每個物品具有一個魔法值,我們用Xi表示編號爲i的物品的魔法值。每個魔法值Xi是不超過n的正整數,可能有多個物品的魔法值相同。

大魔法師認爲,當且僅當四個編號爲a,b,c,d的魔法物品滿足xa < xb < xc < xd,Xb - Xa=2(Xd - Xc),並且xb-xa < (xc-xb)/3時,這四個魔法物品形成了一個魔法陣,他稱這四個魔法物品分別爲這個魔法陣的A物品,B物品,C物品,D物品。

現在,大魔法師想要知道,對於每個魔法物品,作爲某個魔法陣的A物品出現的次數,作爲B物品的次數,作爲C物品的次數,和作爲D物品的次數。

輸入輸出格式

輸入格式:
輸入文件的第一行包含兩個空格隔開的正整數n和m。

接下來m行,每行一個正整數,第i+1行的正整數表示Xi,即編號爲i的物品的魔法值。

保證,,。每個Xi是分別在合法範圍內等概率隨機生成的。

輸出格式:
共輸出m行,每行四個整數。第i行的四個整數依次表示編號爲i的物品作 爲A,B,C,D物品分別出現的次數。

保證標準輸出中的每個數都不會超過10^9。

每行相鄰的兩個數之間用恰好一個空格隔開。

輸入輸出樣例

輸入樣例#1:
30 8
1
24
7
28
5
29
26
24
輸出樣例#1:
4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0
輸入樣例#2:
15 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
輸出樣例#2:
5 0 0 0
4 0 0 0
3 5 0 0
2 4 0 0
1 3 0 0
0 2 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 0 1 0
0 0 2 1
0 0 3 2
0 0 4 3
0 0 5 4
0 0 0 5
說明

【樣例解釋1】

共有5個魔法陣,分別爲:

物品1,3,7,6,其魔法值分別爲1,7,26,29;

物品1,5,2,7,其魔法值分別爲1,5,24,26;

物品1,5,7,4,其魔法值分別爲1,5,26,28;

物品1,5,8,7,其魔法值分別爲1,5,24,26;

物品5,3,4,6,其魔法值分別爲5,7,28,29。

以物品5爲例,它作爲A物品出現了1次,作爲B物品出現了3次,沒有作爲C物品或者D物品出現,所以這一行輸出的四個數依次爲1,3,0,0。

此外,如果我們將輸出看作一個m行4列的矩陣,那麼每一列上的m個數之和都應等於魔法陣的總數。所以,如果你的輸出不滿足這個性質,那麼這個輸出一定不正確。你可以通過這個性質在一定程度上檢查你的輸出的正確性。


解題思路

  • 魔法值相同的魔法物品,他們作爲 a、b、c、d 物品的次數也一定相同,那麼我們就可以利用這一點來解題。
  • 維護數組 a[] b[] c[] d[] , a[i] b[i] c[i] d[i]分別表示的是魔法值爲 i 的魔法物品作爲 a b c d 物品的次數。由於題目中給的 n 範圍較小,因此在這裏我使用桶排序
  • 思考這樣一個問題:
    • 假設存在一對 c d 物品,其對應的 a b 物品的個數分別表示爲 ai bi 。那麼這樣的 a b c d 可以構成的魔法陣的個數爲: ai * bi 。
    • 同樣還是存在這樣的一對 c d物品,由於 b 與 c 物品中間間隔的魔法值是不固定的,假設存在兩組 a b , 其對應的物品個數分別爲 a1i b1i a2i b2i 那麼這樣的 a b c d 可以構成的魔法陣的個數爲 a1i * b1i + a2i * b2i
    • 以此類推,如果存在的a b的個數爲 n ,則其對應的魔法陣的個數爲 a1i * b1i + a2i * b2i + ···· + ani * bni
    • 那麼我們就可以利用這個思想來進行前綴和的優化
  • 下面分析一下題目中所給的三個條件:
    • Xa<Xb<Xc<Xd
      說明這是一個單調遞增的序列
    • XbXa=2(XdXc)
      XbXa<(XcXb)3
      可以得到 :
      2(XdXc)=XbXa
      XcXb>6(XdXc)
    • 如果設
      XdXc=t
      那麼
      XbXa=2t
      XcXb>6t
  • 由於物品的魔法值只能取整數,通過上式中關於 t 的關係我們可以通過枚舉 t 來計算魔法物品的出現次數
  • 如果我們將 t 按照從小到大的順序枚舉,那麼對於相同的 t ,同樣可以按照從小到大的次序枚舉對應的 d 物品的位置.由於
    XcXb>6t
    因此在相同 t 值的情況下,魔法值較小的 c , d 物品對應的 a , b 物品同樣可以與魔法值較大的 c’ , d’ 物品構成魔法陣。通過這個思想來進行前綴和優化
  • 通過枚舉 d 物品的位置,得到對應的 c 物品的位置,利用上述方法計算前綴和得出此魔法值作爲 c d 魔法物品出現的次數
  • 同理枚舉 a 物品的位置,得到對應的 b 物品的位置,再次利用前綴和計算的出此魔法值作爲 a b 魔法物品出現的次數

源代碼

#include<cstdio>
using namespace std;
int n,m;
int v[40010],num[15010];
int a[15010],b[15010],c[15010],d[15010];
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i ++){
        scanf("%d",&v[i]);
        num[v[i]]++;//桶排序
    }
    for(int t = 1; t * 9 < n; t++){ //枚舉t
        int sum = 0;
        int va,vb,vc,vd;
        for(vd = t * 9 + 2; vd <= n; vd++){//枚舉 d 物品的位置
            va = vd - 9 * t - 1;//a 物品的魔法值
            vb = vd - 7 * t - 1;//b 物品的魔法值
            vc = vd - t;//c 物品的魔法值
            sum += num[vb] * num[va]; //前綴和求能組成的魔法陣的個數
            c[vc] += num[vd] * sum;
            d[vd] += num[vc] * sum; 
        }
        sum = 0;
        for(va = n - t * 9 - 1; va >= 1; va--){//枚舉 a 物品的位置
            vb = va + 2 * t;
            vc = va + t * 8 + 1;
            vd = va + t * 9 + 1;
            sum += num[vc] * num[vd];
            a[va] += num[vb] * sum;
            b[vb] += num[va] * sum;
        }
    }
    for(int i = 1; i <= m; i++)
        printf("%d %d %d %d\n", a[v[i]],b[v[i]],c[v[i]],d[v[i]]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章