[NOIP2016普及组]魔法阵

题目:洛谷P2119、Vijos P2012、codevs5624。

题目大意:有n件物品,每件物品有个魔法值。要求组成魔法阵(Xa,Xb,Xc,Xd),该魔法阵要满足Xa<Xb<Xc<Xd,Xb-Xa=2(Xd-Xc),并且Xb-Xa<(Xc-Xb)/3。求每件物品作为a、b、c、d的次数。

解题思路:这真是一道锻炼思(bào)维(lì)能力的好(kēng)题!

首先枚举魔法阵的每件物品,姿势好可得65(洛谷测)。

然后是强(wěi)大(suǒ)的正解。

首先利用桶排的思路保存各个魔法值。

设Xd-Xc=t,则根据一系列推论可得:

Xb-Xa=2t

Xc-Xb>6t

那么首先想到的就是枚举t,然后再枚举c、b,算出a、d,最后一交发现还是超时几个点!!是不是气的想砸电脑了?

别急慢慢来。首先我们可以发现,t的枚举范围在1~n/9中即可。

然后我们令d取满足条件的最小值(9t+2),a就只能取1(b可以根据a推出,不用考虑),那么如果d增加1,a就能选1、2,d增加2,a能取1、2、3……

那么我们用一个sum记录当前a、b共有多少种选法,然后计算c、d,就不用再次枚举了。

同理a、b取最大值,然后如此计算即可。

满分拿到,感觉比网络流还难!!!

C++ Code:

 

#include<cstdio>
#include<cctype>
using namespace std;
int n,m,cnt[15999]={0};
int A[15999]={0},B[15999]={0},C[15999]={0},D[15999]={0},f[40004];
inline int readint(){
	char c=getchar();
	int p=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())p=(p<<3)+(p<<1)+(c^'0');
	return p;
}
int main(){
	m=readint(),n=readint();
	for(int i=1;i<=n;++i){
		f[i]=readint();
		++cnt[f[i]];
	}
	for(int t=1;9*t<=m;++t){
		int sum=0;
		for(int d=t*9+2;d<=m;++d){
			int c=d-t,b=d-7*t-1,a=d-9*t-1;
			sum+=cnt[a]*cnt[b];
			C[c]+=cnt[d]*sum;
			D[d]+=cnt[c]*sum;
		}
		sum=0;
		for(int a=m-t*9-1;a>=1;--a){
			int b=a+2*t,c=a+8*t+1,d=a+9*t+1;
			sum+=cnt[c]*cnt[d];
			A[a]+=cnt[b]*sum;
			B[b]+=cnt[a]*sum;
		}
	}
	for(int i=1;i<=n;++i)printf("%d %d %d %d\n",A[f[i]],B[f[i]],C[f[i]],D[f[i]]);
	return 0;
}

 

posted @ 2017-09-10 20:35  Mrsrz  阅读(1076)  评论(0编辑  收藏  举报