魔法阵

Problem 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物品的次数。

输入格式

  输入文件的第一行包含两个空格隔开的正整数n和m。
  接下来m行,每行一个正整数,第i+1行的正整数表示Xi,即编号为i的物品的魔法值。
  保证1<=n<=15000,1<=m<=40000,1<=xi<=n。每个Xi是分别在合法范围内等概率随机生成的。

Input format

输入文件的第一行包含两个空格隔开的正整数n和m。
接下来m行,每行一个正整数,第i+1行的正整数表示Xi,即编号为i的物品的魔法值。
保证1<=n<=15000,1<=m<=40000,1<=xi<=n。每个Xi是分别在合法范围内等概率随机生成的。

Output format

 共输出m行,每行四个整数。第i行的四个整数依次表示编号为i的物品作 为A,B,C,D物品分别出现的次数。
  保证标准输出中的每个数都不会超过10^9。
  每行相邻的两个数之间用恰好一个空格隔开。

Algorithm design

 Optimized Enumeration

Problem analysis

这题有个很坑的地方:xb-xa<(xc-xb)/3 “/3”是个实数计算

当然可以移到左边化为整型计算

 

然后是思路处理

首先当然是暴力四重枚举

复杂度 O(40000^4)

TLE45

 

发现在枚出abcd任意三者的情况下可以直接求出另一个数字

因为求出的是数值

所以不妨把所有的物品都化成数值

相同数值的只要在hash上累加即可

三重循环复杂度O(40000^3)

TLE85

 

在尝试各种剪枝都无效的情况下

要想新算法

设xd-xc=i,xb-xa=2i,xc-xb>6i

可见i<n/9

再看其实每一组d,c都是可以对应多组a,b存在的

并且在相同i的情况下 在右的d,c是要比左面的对应组数多的

A,b反之

这就可以用前缀和思想来搞

复杂度O(n^2/9)

Source code

 1 #include <bits/stdc++.h>
 2 #define bnd_cnt 40010
 3 #define bnd_mag 15010
 4  
 5 using namespace std;
 6  
 7 int max_mag,cnt_obj,cnt_num,cnt[4][bnd_mag],mag[bnd_cnt],ust_mag[bnd_cnt],cnt_mag[bnd_mag];
 8  
 9 void in()
10 {
11    cin>>max_mag>>cnt_obj;
12    int wst_mag[bnd_cnt];
13    memset(wst_mag,0,sizeof(wst_mag));
14    for(int i=1;i<=cnt_obj;i++)
15    {
16       scanf("%d",&ust_mag[i]);
17       wst_mag[i]=ust_mag[i];
18       cnt_mag[ust_mag[i]]++;
19    }
20    sort(wst_mag+1,wst_mag+cnt_obj+1);
21 //按值排序
22    for(int i=1;i<=cnt_obj;i++)
23       if(wst_mag[i]>wst_mag[i-1])mag[++cnt_num]=wst_mag[i];
24 //覆盖重复值
25    return;
26 }
27   
28 void work()
29 {
30    for(int gap=1;gap*9<max_mag;gap++)
31    {
32       int cnt_left[bnd_mag];
33       int cnt_right[bnd_mag];
34       memset(cnt_left,0,sizeof(cnt_left));
35       memset(cnt_right,0,sizeof(cnt_right));
36       for(int a=1;a+gap*9<max_mag;a++)
37       {
38          int b=a+gap*2;
39          int least_c=b+gap*6+1;
40          cnt_left[least_c]+=cnt_mag[a]*cnt_mag[b];
41       }//处理作为c,d点时的a,b组合数
42       for(int c=gap*8+2;c+gap<=max_mag;c++)
43       {
44          int d=c+gap;
45          int least_b=c-gap*6-1;
46          cnt_right[least_b]+=cnt_mag[c]*cnt_mag[d];
47          cnt_left[c]+=cnt_left[c-1];
48          cnt[2][c]+=cnt_left[c]*cnt_mag[d];
49          cnt[3][d]+=cnt_left[c]*cnt_mag[c];
50       }//同理 并顺路将c,d点处理方案数
51       for(int a=max_mag-9*gap-1;a>=1;a--)
52       {
53          int b=a+gap*2;
54          cnt_right[b]+=cnt_right[b+1];
55          cnt[1][b]+=cnt_right[b]*cnt_mag[a];
56          cnt[0][a]+=cnt_right[b]*cnt_mag[b];
57       }//同理啊
58    }
59    return;
60 }
61  
62 void out()
63 {
64    for(int i=1;i<=cnt_obj;i++)
65    {
66       for(int j=0;j<=3;j++)
67          cout<<cnt[j][ust_mag[i]]<<" ";
68       cout<<endl;
69    }//输出
70    return;
71 }
72  
73 int main()
74 {
75    freopen("magic.in","r",stdin);
76    freopen("magic.out","w",stdout);
77    in();
78    work();
79    out();
80    return 0;
81 }
82  
83  
View Code

over

posted @ 2018-06-08 14:56  Srzer  阅读(245)  评论(0编辑  收藏  举报