魔法阵
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
over