1. 程式人生 > >◆競賽題目◆◇NOIP2016普及組◇ 魔法陣

◆競賽題目◆◇NOIP2016普及組◇ 魔法陣

◇NOIP2016 普及組◇魔法陣

Description
六十年一次的魔法戰爭就要開始了,大魔法師準備從附近的魔法場中汲取魔法能量。
大魔法師有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物品的次數。
Input


輸入檔案的第一行包含兩個空格隔開的正整數n和m。
接下來m行,每行一個正整數,第i+1行的正整數表示Xi,即編號為i的物品的魔法值。
保證1≤n≤15000,1≤m≤40000,1≤Xi≤n。每個Xi是分別在合法範圍內等概率隨機生成的。

Output
共輸出m行,每行四個整數。第i行的四個整數依次表示編號為i的物品作 為A,B,C,D物品分別出現的次數。
保證標準輸出中的每個數都不會超過10^9。
每行相鄰的兩個數之間用恰好一個空格隔開。

Sample Input

30 8
1
24
7
28
5
29
26
24

Sample Output

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

題目解析
對於這道NOIP普及組的第四題,畢竟有些難度。於是大多數人追求的是騙分,而在比賽後做這道題,我們瞭解到了正解——仍然是列舉。
首先涉及一定的 數學分析 。看到:“Xa < Xb < Xc < Xd,Xb - Xa = 2 ( Xd - Xc ),並且 Xb - Xa < ( Xc - Xb ) / 3”,我們可以先整合一下:

  1. 先把 XbXa=2(XdXc) 代入 XbXa<(XcXb)/3,得到 2(XdXc)<(XcXb)/3,再同時乘3,得 6(XdXc)<XcXb
  2. 我們把它用幾何語言解釋一下,就是:在數軸上,a 到 b 的距離是 d 到 c 的距離的2倍;6倍的 c 到 d 的距離小於 b 到 c 的距離;
  3. 我們把 d 到 c 的距離記為 i ,那麼 ba=2i,cb>6i,dc=i
  4. 表現在數軸上:
    這裡寫圖片描述

由於C++最方便計算整數,因此 >6i我們可以略為6i+1 。因此我們就得到 cb=6i+1
再回到程式上——首先是幾個陣列:

1. 在這裡物品的序號並沒有任何的影響,我們可以用 int 陣列 magic[i] 表示魔法值為i的物品的總數;
2. 還需要一個存答案的 int 陣列 ans[i][j] 表示魔法值為i的物品作為 a,b,c,d(0,1,2,3)的次數;

先列舉i的值。因為 d<=n ,所以 da<=n,即 2i+i+6i+1<=n,就得到了i的範圍 i<=(n1)/9,因為 i=n/9 時對答案沒有任何影響,我們可以寫成 i<=n/9
由於確定了a就可以確定b,確定了d就可以確定c,根據組合數學的乘法原理,我們可以用一個字首和sum變數來儲存每一個a的值所對應的 c*d 的字首和或每一個d的值所對應的 a*b 的字首和。又由於 a 與 b 相關, c 與 d 相關,於是就可以求得每一個a或d的值所對應的物品應用次數。
不多說,看程式碼。

程式樣例

/*Lucky_Glass*/
/*$魔法陣$*/
#include<cstdio>
#include<vector>
#include<algorithm>
#define M_max 15000
#define T_max 40000
using namespace std;
int read(){int x=0;char ch=getchar();while(ch<'0' || ch>'9') ch=getchar();while('0'<=ch && ch<='9') x=x*10+ch-'0',ch=getchar();return x;}//讀入優化
int magic[M_max+5];
int T_num,M_num,ans[M_max+5][4],F[T_max+5];
int main()
{
    scanf("%d%d",&M_num,&T_num);
    for(int i=1;i<=T_num;i++) F[i]=read(),magic[F[i]]++;
    for(int i=1;i<=M_num/9;i++)
    {
        for(int d=i*9+2,sum=0;d<=M_num;d++)
        {
            int c=d-i,b=d-7*i-1,a=d-9*i-1;
            sum+=magic[a]*magic[b];
            ans[c][2]+=magic[d]*sum;
            ans[d][3]+=magic[c]*sum;
        }
        for(int a=M_num-i*9-1,sum=0;a>=1;a--)
        {
            int b=a+2*i,c=a+8*i+1,d=a+9*i+1;
            sum+=magic[c]*magic[d];
            ans[a][0]+=magic[b]*sum;
            ans[b][1]+=magic[a]*sum;
        }
    }
    for(int i=1;i<=T_num;i++)
        printf("%d %d %d %d\n",ans[F[i]][0],ans[F[i]][1],ans[F[i]][2],ans[F[i]][3]);
    return 0;
}

The End

Thanks for reading!

-Lucky_Glass