[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; }