传送门:CodeVS5624
算法一(送分算法):
最朴素的算法应该就是四重循环暴力枚举每一个 $x_a,x_b,x_c,x_d$
当然只能通过较少的点
于是$O(n^4)$
算法二(骗分算法):
由算法一改变一下便可以省下一重循环
暴力出 $x_a,x_b,x_c ,然后计算出 x_d$
于是$O(n^3)$
当然还可以加一些常数上的优化 比如确定 $x_a,x_b 后,x_c$ 其实范围已经缩小了
算法三(似乎可以AC):
可以算是一个折半枚举吧
用 $ans^{xy}$ 记录 数x(注意不是第x个)在位置y上出现的次数
那么枚举差值 i ,
再枚举 $x_a$
则 :
$x_b=x_a+i*2;$
$x_c>x_b+i*6=x_a+i*8;$
$x_d=x_c+i;$
换句话说 $x_b$ 已确定下来
然后再来研究一下 $x_c,x_d$
当 $x_c$ 确定下来后,$x_d$ 也确定下来
并且随着 $x_a$ 的增大 $x_c$ 的范围单调递减(反过来就是随着 $x_a$ 的增大,$x_c$ 的范围单调递增)
于是就可以利用这一性质求出 $sum=\sum(f[x_c]*f[x_d])$
然后就能累加到 $ans[x_a][1] 和 ans[x_b][2]$ 上
同理累加 $ans[x_c][3] 和 ans[x_d][4]$
详见代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,sum,A,B,C,D,a[40005],f[20005],ans[20005][5]; int main(){ cin>>n>>m; for (int i=1;i<=m;i++){ cin>>a[i]; f[a[i]]++; } for (int i=1;i<=2000;i++){ sum=0; for (A=n-9*i-1;A>=1;A--){ B=A+2*i; C=A+8*i+1; D=A+9*i+1; sum+=f[C]*f[D]; ans[A][1]+=f[B]*sum; ans[B][2]+=f[A]*sum; } sum=0; for (C=2+8*i;C<=n;C++){ A=C-8*i-1; B=C-6*i-1; D=C+i; sum+=f[A]*f[B]; ans[C][3]+=f[D]*sum; ans[D][4]+=f[C]*sum; } } for (int i=1;i<=m;i++) cout<<ans[a[i]][1]<<' '<<ans[a[i]][2]<<' '<<ans[a[i]][3]<<' '<<ans[a[i]][4]<<'\n'; return 0; }
|