#优化枚举#洛谷 2119 魔法阵
分析
首先显然与物品数量无关,用一个桶记录每个数的出现次数即可
题目的数据随机告诉我们\(O(n^2)\)的算法可能能够卡过去,但显然要找一些更优的算法
然后研究一下性质,发现\(x_b-x_a\)出现了两次,那存不存在一个量可以尽量表达\(x_a,x_b,x_c,x_d\)
考虑枚举\(t=x_d-x_c\),因为它是\(x_b-x_a\)的\(\frac{1}{2}\)倍,所需要枚举的范围可能会更小
那么\(x_b-x_a=2t,x_c-x_b>6t\),那我枚举的\(t\)只需要在\((n-1)/9\)内就行了
那肯定还要枚举一个数,由于\(x_c-x_b\)不能完全确定,不妨分成两种情况:
- 当枚举\(x_a\)时,求\(x_a\)和\(x_b\)的方案数,那\(x_c\)和\(x_d\)其实可以用后缀和表示
- 当枚举\(x_d\)时,求\(x_c\)和\(x_d\)的方案数,那\(x_a\)和\(x_b\)其实可以用前缀和表示
时间复杂度\(O(\frac{1}{9}n^2)\),实际上常数非常小
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=40011;
int m,n,a[N],c[N],ans[4][N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
signed main(){
m=iut(); n=iut();
for (rr int i=1;i<=n;++i) ++c[a[i]=iut()];
for (rr int i=1;i*9<m;++i){
for (rr int D=9*i+2,s=0;D<=m;++D){
rr int C=D-i,B=C-6*i-1,A=B-2*i;
s+=c[A]*c[B],ans[2][C]+=c[D]*s,ans[3][D]+=c[C]*s;
}
for (rr int A=m-9*i-1,s=0;A;--A){
rr int B=A+2*i,C=B+6*i+1,D=C+i;
s+=c[C]*c[D],ans[0][A]+=c[B]*s,ans[1][B]+=c[A]*s;
}
}
for (rr int i=1;i<=n;++i)
for (rr int j=0;j<4;++j)
print(ans[j][a[i]]),putchar(j==3?10:32);
return 0;
}