#优化枚举#洛谷 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\)不能完全确定,不妨分成两种情况:

  1. 当枚举\(x_a\)时,求\(x_a\)\(x_b\)的方案数,那\(x_c\)\(x_d\)其实可以用后缀和表示
  2. 当枚举\(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;
}
posted @ 2020-10-07 07:41  lemondinosaur  阅读(95)  评论(0编辑  收藏  举报