[NOIP2016普及组]魔法阵(枚举暴力)
很大程度上借鉴了Henry_y大佬的博客,一些地方改成了自己看得懂的版本。
拿到这道题先打了一个 \(\mathcal{O(n^4)}\) 的暴力,因为实现的比较好+洛谷神机,跑到了 65 分,具体操作有:
- 记录编号并排序(可以用
pair
数组实现),\(b\) 可以从 \(a+1\) 开始枚举,\(c\) 可以从 \(b+1\) 开始枚举,以此类推; - 枚举完 \(b,c,d\) 后立刻一个
if
判断是否满足 \(X_a<X_b<X_c<X_d\); - 枚举完 \(c\) 后立即判断 \(3(X_b-X_a)<X_c-X_b\),注意化除为乘。
似乎还有乱搞的空间,例如枚举完 \(a,b,c\) 后 \(d\) 已经确定了,可以用记录一些奇怪的东西继续卡。然而暴力有时尽,我们来看正解。
反正是要枚举的,我们就从三个式子出发,看看是不是有什么性质。设 \(X_d-X_c=t\),于是有 \(X_b-X_a=2t,X_c-X_b>6t\)。若令 \(X_c-X_b=6t+k\)(补上剩下的那一块),则有关系图如下:
自然想到枚举 \(t\),因为 \(k\ge 1\),所以保证 \(1\le 9t<n\) 即可。再枚举 \(d\),即可确定 \(c\) 的位置:\(c=d-t\)。对于这一组 \(c,d\),\(a,b\) 的最大取值为 \(d-9-1,d-7t-1\)。设 \(cnt_x\) 表示值 \(x\) 出现的次数(就是个桶),则这组解对答案的贡献为 \(cnt_a\times cnt_b\),乘法原理。
如果 \(a,b\) 更小,也一定成立(满足 \(X_c-X-b>6t\))。不难想到前缀和优化,累计一个 \(sum=\sum_{a\le a_{max},b\le b_{max}} cnt_a\times cnt_b\),每次 C[c]+=sum*cnt[d],D[d]+=sum*cnt[c];
即可。
计算 \(a,b\) 同理,改成枚举 \(a\),反向跑,记录后缀和(\(cnt_c\times cnt_d\))。
下面是 AC 代码:
int main(){
int n,m; read(n),read(m);
for(int i=1;i<=m;++i) read(x[i]),++cnt[x[i]];
for(int t=1,sum;t*9<n;++t){
sum=0;
for(int d=t*9+2;d<=n;++d){
int a=d-t*9-1,b=d-t*7-1,c=d-t;
sum+=cnt[a]*cnt[b];//前缀和
C[c]+=sum*cnt[d],D[d]+=sum*cnt[c];//乘法原理,下同
}
sum=0;
for(int a=n-t*9-1;a;--a){
int b=a+t*2,c=a+t*8+1,d=a+t*9+1;
sum+=cnt[c]*cnt[d];//后缀和
A[a]+=sum*cnt[b],B[b]+=sum*cnt[a];
}
}
for(int i=1;i<=m;++i){
write(A[x[i]]),PT,write(B[x[i]]),PT,
write(C[x[i]]),PT,write(D[x[i]]),EN;//注意此时ABCD为值的出现次数,不是编号
}
return 0;
}