【NOIP普及组2016】&魔法阵 This is magic!&
2017-10-20 13:42
148 查看
2016压轴题-魔法阵
2016压轴题-魔法阵前言
题目描述
题目分析-暴力枚举Om4
开始优化-桶思想优化On3
高端操作-学不来的数学分析On2
END
前言
听闻老前辈们道这道题好像很难的样子,于是我就去做了……然后我就TLE了
于是偷偷瞟了一眼大老前辈们的博客,发现这道题好像,还是枚举,只是有用到【数学方法】优化罢了
完了完了,一提到数学,我脑子里顿时腾起了层层云雾,所以最后决定还是来写写东西。
来吧,欢迎进入MAGIC的世界
题目描述
六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。大魔法师有m个魔法物品,编号分别为1,2,...,m。每个物品具有一个魔法值,我们用Xi表示编号为i的物品的魔法值。每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。
大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足xa<xb<xc<xd,xb−xa=2(xd−xc),并且xb−xa<(xc−xb)/3时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的A物品,B物品,C物品,D物品。
现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的A物品出现的次数,作为B物品的次数,作为C物品的次数,和作为D物品的次数。
输入
输入文件的第一行包含两个空格隔开的正整数n和m。
接下来m行,每行一个正整数,第i+1行的正整数表示Xi,即编号为i的物品的魔法值。
保证1≤n≤150001≤n≤15000,1≤m≤400001≤m≤40000,1≤Xi≤n1≤Xi≤n。每个Xi是分别在合法范围内等概率随机生成的。
输出
共输出m行,每行四个整数。第i行的四个整数依次表示编号为i的物品作 为A,B,C,D物品分别出现的次数。
保证标准输出中的每个数都不会超过10^9。
每行相邻的两个数之间用恰好一个空格隔开。
样例输入
输入样例#1:
30 8
1
24
7
28
5
29
26
24
输入样例#2:
15 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
样例输出
输出样例#1:
4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0
输出样例#2:
5 0 0 0
4 0 0 0
3 5 0 0
2 4 0 0
1 3 0 0
0 2 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 0 1 0
0 0 2 1
0 0 3 2
0 0 4 3
0 0 5 4
0 0 0 5
样例说明
样例#1:
共有5个魔法阵,分别为:
物品1,3,7,6,其魔法值分别为1,7,26,29;
物品1,5,2,7,其魔法值分别为1,5,24,26;
物品1,5,7,4,其魔法值分别为1,5,26,28;
物品1,5,8,7,其魔法值分别为1,5,24,26;
物品5,3,4,6,其魔法值分别为5,7,28,29。
以物品5为例,它作为A物品出现了1次,作为B物品出现了3次,没有作为C物品或者D物品出现,所以这一行输出的四个数依次为1,3,0,0。
此外,如果我们将输出看作一个m行4列的矩阵,那么每一列上的m个数之和都应等于魔法阵的总数。所以,如果你的输出不满足这个性质,那么这个输出一定不正确。你可以通过这个性质在一定程度上检查你的输出的正确性。
题目分析-暴力枚举O(m^4)
“这么简单,当然是暴力啦~”于是一段清晰的思路像一片Sunshine:分别枚举A,B,C,D,判断合法。
条件反射地想到,可以剪枝!在找A,B,C,D及时判断是否合法,可以省去多多的状态。
然后写出来以后,突然瞟了一眼数据范围……
真是莫名其妙。
一段十分失败的枚举代码:
#include<cstdio> int X[40005]; int ansA[40005],ansB[40005],ansC[40005],ansD[40005]; int main() { int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) scanf("%d",&X[i]); for(int A=1;A<=m;A++) { for(int B=1;B<=m;B++) { if( X[A] >= X ) continue; for(int C=1;C<=m;C++) { if( X[B] >= X[C] ) continue; if( 3 * (X[B] - X[A]) >= X[C] - X[B] ) continue; for(int D=1;D<=m;D++) { if( X[C] >= X[D] ) continue; if( X[B] - X[A] != 2*(X[D] - X[C]) ) continue; ansA[A]++;ansB[B]++;ansC[C]++;ansD[D]++; } } } } for(int i=1;i<=m;i++) printf("%d %d %d %d\n",ansA[i],ansB[i],ansC[i],ansD[i]); }//期望35%-实得55%
开始优化-桶思想优化O(n^3)
再次扫一眼题目,我们发现n的范围要远远小于m的范围,也就是说有大部分的数据魔法值其实是相同的。从m出发的话,给你O(m2)你都不一定过得了。于是我们便想到,如果[b]“从n入手”行不行?
不妨按照魔法值来存储对应魔法值的物品数量,计算方案数时采用乘法原理,输出时访问魔法值对应的值。一段稍带迷雾的代码在我眼前渐渐浮现。再计算一下时间复杂度:O(n4)
还能优化吗?可以。当我们确定A、B、C时,实际上根据Xb−Xa=2(Xd−Xc)就已经可以确定D了。内层循环再一次被省略掉了。O(n3)。
感觉考试的时候只能够撑到这里了,再深层次的话……【吐血】
得到差不多一般般的代码
#include<cstdio> int X[40005];int K[15005]; int ansA[15005],ansB[15005],ansC[15005],ansD[15005]; int main() { int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d",&X[i]); K[X[i]]++; } for(int A=1;A<=n;A++) { if( K[A] == 0 ) continue; for(int B=A+1;B<=n;B++) { if( K == 0 ) continue; if( ( B - A ) % 2 == 1 ) continue; for(int C=(B-A)*3+B+1;C<=n;C++) { if( K[C] == 0 ) continue; int D = C + ( B - A ) / 2; if( D > n ) continue; if( K[D] == 0 ) continue; ansA[A]+=(K[B]*K[C]*K[D]); ansB[B]+=(K[A]*K[C]*K[D]); ansC[C]+=(K[A]*K[B]*K[D]); ansD[D]+=(K[A]*K[B]*K[C]); } } } for(int i=1;i<=m;i++) printf("%d %d %d %d\n",ansA[X[i]],ansB[X[i]],ansC[X[i]],ansD[X[i]]); }//期望80%-实得85%
高端操作-学不来的数学分析O(n^2)
先放代码镇镇大佬们的英魂。#include<cstdio> #include<cstring> int X[16000],a[40005],S[16000]; int A[40005],B[40005],C[40005],D[40005]; int main() { int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d",&a[i]); X[a[i]]++; } for(int i=1;i*9<n;i++) { memset(S,0,sizeof(S)); for(int j=2*i+1;j<=n-7*i-1;j++) S[j] = X[j - 2*i] * X[j]; for(int j=1;j<=n;j++) S[j] += S[j-1]; for(int j=9*i+1;j<=n;j++) D[j] += S[j - 7*i - 1] * X[j - i]; for(int j=7*i+1;j<=n-i;j++) C[j] += S[j - 6*i -1] * X[j + i]; memset(S,0,sizeof(S)); for(int j=9*i+1;j<=n;j++) S[j] = X[j - i] * X[j]; for(int j=n;j>=1;j--) S[j] += S[j+1]; for(int j=2*i+1;j<=n-7*i-1;j++) B[j] += S[j + 7*i + 1] * X[j - 2*i]; for(int j=1;j<=n-9*i-1;j++) A[j] += S[j + 9*i +1] * X[j + 2*i]; } for(int i = 1;i<=m;i++) printf("%d %d %d %d\n",A[a[i]],B[a[i]],C[a[i]],D[a[i]]); } //期望100%
再发一张大佬用的分析图
恩,我们所知道的条件已经全部标在图上了
啥你看不懂?那我稍微解释解释:
图中的直线【你要相信我这真的不是线段】叫做数轴【对不起我以为你不知道】,轴上的点A,B,C,D的值分别表示A,B,C,D的魔法值xa,xb,xc,xd。
xa<xb<xc<xd,所以A,B,C,D是从左到右放置的
xb−xa=2(xd−xc),[b]“-”的几何意义为两点间的距离。如果确定点的左右关系,绝对值可以去掉,改为右减左。设C,D两点间的距离=xd−xc=i,所以我们可以得到A,B两点间的距离=xb−xa=2∗i
xb−xa<(xc−xb)/3,就是这个不等关系最恶心,如果是等量关系就会简单得多。通过这个不等量关系,我们可以得到:B,C两点间的距离=xc−xb>6∗i。
感觉卡住了……怎么办?
还是从时间复杂度的角度入手吧:
压到O(1)?想多了
压到O(n)||O(m)?即使确定a,b,c,d,i,这里有个不等关系所以也不好办。
看来只能压到O(n2)了。
我们如果确定了D与i,于是B的范围也就能确定了。
反过来,如果我们确定了在某个范围内的B,于是……?
好像有点希望了?!
令a[j]=”(B==j)时选择A、B的方案数”,S[j]=”(B<=j)时A、B的方案数”,则a[j]=X[j]∗X[j−2∗i],显然我们有S[j]=S[j−1]+a[j],于是我们便可以在O(n)的时间内算出S数组
可以得到确定D=t时,方案数 = S[t−7∗i−1]∗X[j−i],即A、B的方案数与C点的方案数累乘。O(n)。
同理,也可在O(n)内求出A=t,B=t,C=t的方案数。
woc这么高大上的吗!
果然NOIP普及压轴题都是思维复杂,挑战脑力,代码极其* *的题吗!
END
THANKS FOR READING THERE!就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~
相关文章推荐
- noip2016普及组T4 魔法阵(magic)(错误)
- NOIP2016 普及组第四题 魔法阵magic 题解
- noip2016普及组复赛第四题 T4魔法阵
- 【原创】【NOIP2016普及组】魔法阵
- NOIP2016普及组第四题——魔法阵
- 2105. 【NOIP2016普及组复赛】魔法阵
- ◆竞赛题目◆◇NOIP2016普及组◇ 魔法阵
- magic(NOIP2016普及组复赛)
- NOIP2016普及组第四题——魔法阵
- [NOIP2016普及] 魔法阵
- 【NOIP2016普及组】复赛——魔法阵
- 【NOIP2016普及组】解题报告 买铅笔 回文日期 海港 魔法阵
- 【noip2016普及】魔法阵
- 【统计】NOIP2016普及组[魔法阵]题解
- NOIP2016普及组第四题——魔法阵
- 【NOIP2016】普及组魔法阵
- noip2016提高&普及组简要题解
- 【NOIP2016普及组复赛】魔法阵
- NOIP2016普及组第四题魔法阵解说+水法
- 初中OJ2105【NOIP2016普及组复赛】魔法阵