首页 > 其他分享 >CSP历年复赛题-P2119 [NOIP2016 普及组] 魔法阵

CSP历年复赛题-P2119 [NOIP2016 普及组] 魔法阵

时间:2024-06-07 10:11:06浏览次数:22  
标签:P2119 NOIP2016 魔法值 Xb Xc Xa 魔法阵 Xd int

原题链接:https://www.luogu.com.cn/problem/P2119

题意解读:在一组数里找出所有的Xa,Xb,Xc,Xd的组合,使得满足Xa<Xb<Xc<Xd, Xb-Xa=2(Xd-Xc), Xb-Xa<(Xc-Xb)/3,并统计出每个数作为A,B,C,D出现的次数。

解题思路:

1、枚举(O(n^4))

首先想到的是通过4重循环枚举所有可能的Xa,Xb,Xc,Xd,然后判断是否符合需要满足的条件,如果符合,就将这一组Xa,Xb,Xc,Xd所谓有效数字累计到答案A[Xa]++, B[Xb]++, C[Xc]++, D[Xd]++,A,B,C,D数组记录各个魔法值作为A,B,C,D出现的次数。显然已知4个变量之间的等式,没有必要4重循环枚举,只需要枚举3个变量即可,所以此代码就不写了。

2、枚举(O(n^3))

根据题目要求,魔法值一共有40000个,但每个魔法值不超过15000,也就是有很多同样的魔法值,那么相同的魔法值在形成Xa,Xb,Xc,Xd的组合时效果是一样的,没必要一一枚举,可以考虑去重。

我们设int magic[M]存储每种物品的魔法值,int h[N]记录每种魔法值对应的物品数,vector<int> magic2为去重排序后的魔法值

h[N]是桶数组,一方面可以记录每种魔法值的数量,另一方面可以起到计数排序的作用,因此我们对去重排序后的魔法值magic2进行枚举即可。

先看Xa<Xb<Xc<Xd, 枚举是可以先枚举Xa,再枚举Xb,然后枚举Xc,Xd可以计算得来

再看Xb-Xa=2(Xd-Xc), 可以有两个结论:a、Xb-Xa是偶数,这样在枚举中可以提前判断 b、Xd = (Xb - Xa + 2 * Xc) / 2

最后Xb-Xa<(Xc-Xb)/3,经过移项可得:Xc > 4 * Xb - 3 * Xa,可以在枚举到Xc时进行提前判断

由于每种魔法值有多个,而枚举对象的已经去重之后的魔法值,所以在找到一组有效的Xa, Xb, Xc, Xd时,如何计算作为A,B,C,D出现的次数呢?

根据乘法原理:

Xa作为A出现的次数是由Xb,Xc,Xd的个数决定的,从h[Xb]个Xb中选1个,从h[Xc]个Xc中选1个,从h[Xd]个Xd中选1个都可以和Xa组成有效的组合,

所以有A[Xa] += h[Xb] * h[Xc] * h[Xd]

同理,

B[Xb] += h[Xa] * h[Xc] * h[Xd] C[Xc] +=h[Xa] *h[Xb] *h[Xd] D[Xd] +=h[Xa] *h[Xb] *h[Xc] 最后,按输入顺序枚举每一种魔法值,输出其作为A,B,C,D出现的次数 这样编写的代码就能得到90分,如果在考试中,够用了。

90分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 15005, M = 40005;
int n, m;
int A[N], B[N], C[N], D[N]; //每种魔法值作为A、B、C、D的次数
int magic[M]; //每种物品的魔法值
int h[N]; //每种魔法值对应的物品数
vector<int> magic2; //去重排序后的魔法值

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> magic[i];
        h[magic[i]]++;
    }
    for(int i = 1; i <= N; i++)
    {
        if(h[i] > 0) magic2.push_back(i);
    }
    for(int i = 0; i < magic2.size(); i++) //枚举Xa
    {
        int Xa = magic2[i];
        for(int j = i + 1; j < magic2.size(); j++) //枚举Xb
        {
            int Xb = magic2[j];
            if((Xb - Xa) % 2 == 1) continue; //Xb-Xa=2(Xd-Xc),所以必须是偶数
            for(int k = j + 1; k < magic2.size(); k++) //枚举Xc
            {
                int Xc = magic2[k];
                if(Xc <= 4 * Xb - 3 * Xa) continue; //Xa、Xb、Xc要符合关系
                int Xd = (Xb - Xa + 2 * Xc) / 2; //根据公式计算Xd
                if(h[Xd])
                {
                    //Xa,Xb,Xc,Xd魔法值作为A,B,C,D的个数,要取决于其他魔法值的个数,根据乘法原理相乘
                    A[Xa] += h[Xb] * h[Xc] * h[Xd];
                    B[Xb] += h[Xa] * h[Xc] * h[Xd];
                    C[Xc] += h[Xa] * h[Xb] * h[Xd];
                    D[Xd] += h[Xa] * h[Xb] * h[Xc];
                }
            }
        }
    }

    for(int i = 1; i <= m; i++)
    {
        cout << A[magic[i]] << " " << B[magic[i]] << " " << C[magic[i]] << " " << D[magic[i]] << endl;
    }

    return 0;
}

3、枚举(O(n^2/9))

此题要得到100分,就需要换一种思维方式

已知:

Xa<Xb<Xc<Xd,

Xb-Xa=2(Xd-Xc),

Xb-Xa<(Xc-Xb)/3   =>   Xc-Xb > 3(Xb-Xa)  

我们设t = Xd-Xc,则有Xb-Xa=2t,Xc-Xb > 6t,所以Xa,Xb,Xc,Xd的大致分布为:

因此,我们在统计所有符合要求的Xc,Xd时只需要枚举两个变量:t、Xd

9t < n - 1,即9t + 2 <= n

Xd > Xa + 9t,所以 1 + 9t < Xd <= n

Xc = Xd - t

所以,从小到大枚举所有的t、Xd,Xc可以计算出来,而符合要求的Xa,Xb就是Xc,Xd左边所有能放得下的Xa,Xb

这里可以不用一一枚举xa,xb的位置,只需要限定Xb与Xc的距离,最小为6t+1,这样随着Xd,Xc的枚举右移,Xa,Xb也往右移动,

将Xa,Xb对结果的贡献用前缀和记录下来,每次更新

sum_ab += h[Xa] * h[Xb]

C[Xc] += h[Xd] * sum_ab

D[Xd] += h[Xc] * sum_ab

同理,在统计所有符合要求的Xa,Xb是只需要枚举两个变量:t、Xa

这次要从右往左枚举Xa

9t + 2 <= n

1<=Xa<=n-9t-1

sum_cd += h[Xc] * h[Xd]

C[Xc] += h[Xd] * sum_cd

D[Xd] += h[Xc] * sum_cd

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 15005, M = 40005;
int n, m;
int A[N], B[N], C[N], D[N]; //每种魔法值作为A、B、C、D的次数
int magic[M]; //每种物品的魔法值
int h[N]; //每种魔法值对应的物品数
vector<int> magic2; //去重排序后的魔法值

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> magic[i];
        h[magic[i]]++;
    }
    for(int i = 1; i <= N; i++)
    {
        if(h[i] > 0) magic2.push_back(i);
    }
    for(int t = 1; 9 * t + 2 <= n; t++)
    {
        int sum_ab = 0;
        for(int Xd = 9 * t + 1; Xd <= n; Xd++)
        {
            int Xa = Xd - 9 * t - 1;
            int Xb = Xa + 2 * t;
            int Xc = Xd - t;
            sum_ab += h[Xa] * h[Xb];
            C[Xc] += h[Xd] * sum_ab;
            D[Xd] += h[Xc] * sum_ab;
        }

        int sum_dc = 0;
        for(int Xa = n - 9 * t - 1; Xa >= 1; Xa--){ //注意顺序
            int Xb = Xa + t * 2;
            int Xd = Xa + 9 * t + 1;
            int Xc = Xd - t;
            sum_dc += h[Xc] * h[Xd];
            A[Xa] += sum_dc * h[Xb];
            B[Xb] += sum_dc * h[Xa];
        }
    }

    for(int i = 1; i <= m; i++)
    {
        cout << A[magic[i]] << " " << B[magic[i]] << " " << C[magic[i]] << " " << D[magic[i]] << endl;
    }

    return 0;
}

 

标签:P2119,NOIP2016,魔法值,Xb,Xc,Xa,魔法阵,Xd,int
From: https://www.cnblogs.com/jcwy/p/18236639

相关文章

  • CSP历年复赛题-P2058 [NOIP2016 普及组] 海港
    原题链接:https://www.luogu.com.cn/problem/P2058题意解读:计算24小时时间窗口内不同国家的数量,是队列的典型应用。解题思路:本题需要用到两个关键的数据结构:队列、数组队列用来保存24小时内到达的船的时间,数组用来保存24小时内每个国家有多少人每到一只船,需要把时间放入队列,如......
  • CSP历年复赛题-P2010 [NOIP2016 普及组] 回文日期
    原题链接:https://www.luogu.com.cn/problem/P2010题意解读:计算两个日期之间有多少个日期是回文。解题思路:如果通过枚举两个日期之间的所有日期,然后判断回文,则会有几个问题:枚举数据规模在10^7级别,再加上对于日期加一天、判断回文等处理,有可能超时,而且对日期进行加一天、判断回......
  • P2831 [NOIP2016 提高组] 愤怒的小鸟
    思路状压+优化代码#include<iostream>#include<stdio.h>#include<algorithm>#include<string>#include<cmath>#include<string.h>#defineR(x)x=read()#defineFor(i,j,n)for(inti=j;i<=n;++i)usingnamespac......
  • 洛谷之P1563 [NOIP2016 提高组] 玩具谜题
    题目 [NOIP2016提高组]玩具谜题题目背景NOIP2016提高组D1T1 题目描述小南有一套可爱的玩具小人,它们各有不同的职业。有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图: 这时singer告诉小南一个谜......
  • 洛谷题单指南-线性表-P2058 [NOIP2016 普及组] 海港
    原题链接:https://www.luogu.com.cn/problem/P2058题意解读:计算24小时时间窗口内不同国家的数量,是队列的典型应用。解题思路:本题需要用到两个关键的数据结构:队列、数组队列用来保存24小时内到达的船的时间,数组用来保存24小时内每个国家有多少人每到一只船,需要把时间放入队列,如......
  • P1563 [NOIP2016 提高组] 玩具谜题
    1.题目介绍2.题解2.1模拟思路有一个大坑,题目给你的小人顺序是按逆时针给的,不是顺时针!!!跟顺时针相比掉一下顺序就行。看似一共有四种情况:[0,0],[0,1],[1,0],[1,1],其实可以简化分为两种情况,因为[0,0]和[1,1]都代表你要顺时针数,[1,0],[0,1]都代表你要逆时针数代码#include<b......
  • Luogu P1563 [NOIP2016 提高组] 玩具谜题
    [NOIP2016提高组]玩具谜题\(link\)题目背景NOIP2016提高组D1T1题目描述小南有一套可爱的玩具小人,它们各有不同的职业。有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图:这时singer告诉小南一个谜题:“......
  • 洛谷题单指南-模拟和高精度-P1563 [NOIP2016 提高组] 玩具谜题
    原题链接:https://www.luogu.com.cn/problem/P1563题意解读:本题关键在于根据小人的朝向和寻找的方向来确定数组下标的变化。用数组存储小人,intd[]存朝向,inta[]存名称,朝向和寻找方向有4种组合:朝向(0:向内,1:向外)  寻找方向(0:左,1:右)  数组下标操作00顺时针寻找,下标递减......
  • 小樱的魔法阵
    importturtleastimporttimedeftcyuan(x,y,r):t.fillcolor("black")t.begin_fill()t.seth(0)y=y-rt.penup()t.goto(x,y)t.pendown()t.circle(r)t.end_fill()defyuan(x,y,r):t.seth(0)y......
  • 【洛谷 P1909】[NOIP2016 普及组] 买铅笔 题解(打擂台法)
    [NOIP2016普及组]买铅笔题目背景NOIP2016普及组T1题目描述P老师需要去商店买支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,P老师决定只买同一种包装的铅笔。商店不允许将铅笔的包装......