zoukankan      html  css  js  c++  java
  • 【数论专题】——3318. Brunhilda的生日

    题目描述:

    除去对铁质盔甲强烈的热爱,Brunhilda是一个正常的7岁女孩。近期,她正在策划一个完美的生日派对。她发明了如下的一个游戏:所有的孩子在一个数k被宣读之前不停地跑来跑去。当这个数字k宣读后,所有的孩子将形成人数恰好为k的若干群体,且保证剩余的孩子数目小于k。最后,这不足k个的孩子将从游戏中被淘汰。紧接着,比赛将继续进行,并公布一个新的数字k。游戏将在所有的孩子都被淘汰后结束。

    Brunhilda请她的父亲Wotan在游戏中来宣读数字。Wotan不喜欢这个游戏,当然也不希望在游戏的第一轮就宣布一个正无穷(PS:宣布正无穷等于将所有的孩子都从游戏中淘汰)。 Brunhilda认为这在派对上是相当尴尬的情形,所以她给了她父亲一串共计m个素数的列表。这样,她的父亲便可以从中进行选择。当然,相同的数字在游戏中可以被多次宣读。

     Wotan想尽快结束比赛,因为他有一张他最喜欢的足球俱乐部 FC Asgard的比赛门票。不幸的是,Brunhilda不知道派对上参加游戏的孩子数目。现在,对于Q个不同的数n1,...,nQ个儿童,Wotan要预先知道他所需宣读的最少数字,以便他尽早结束游戏。


    第一行包含整数m和Q。

    第二行包含m个不同的递增素数pi(1≤i≤M),表示Wotan可以宣读的数字。

    接下来Q行分别包含一个整数nj(1≤j≤Q),表示可能参加游戏的孩子数目nj。

    输出包括Q行。第j行表示对于询问nj所得到的答案,即如果Wotan能结束游戏,请输出他最少所需要宣读的数字个数,否则输出字符串oo(两个小写字母o表示∞)。


    我们可以先考虑一下,如果儿童数有5,此时有2,3两种命令,就可以有几种情况:

    5-5%3=3,3-3%2=2,2-2%3=0;

    5-5%2=4,4-4%3=3,3-3%2=2,2-2%3=0;

    总之最小情况是3.

    不难发现,每次操作可以变成一个公式:

    设有n个儿童,则为n-n%p[j]。

    我们要求的就是这个方程:

    时间复杂度:O(NM),考虑优化:

    1.函数单调不减,所以要满足(x-x mod p[j])越小越好.

    设x-x mod p[j]=k,令 x=ap[j]+b,则原式化为:

    ap[j]+b-(ap[j]+b) mod p[j]=k.

    ap[j]+b-b=k.

    ap[j]=k.

    所以p[j]为k的一个质因数。

    所以k如果不是p[j]的倍数的话是无法推出下一个答案的.

    同时,设下一个状态为y,对于每一个y,y<x+p[j],必须同时满足上面两个条件才能由f(x)转移到f(y)。

    对于每一个y,其对应的x唯一。

    所以维护p[i]对应的x,用堆来维护它们的最小值。

    O(N*log2M)

    2.维护最小值的另外一种方法:

    对于每一个x,只要有一个p[j]可以使它不失效它就不会失效,所以p[j]越大越好(就能保证有更大的范围选出最小值),用线性筛求出每个数s最大时p[i]的倍数,设其为max_prime(q),若s=tk,则max_prime=max(k,max_prime(t)),接着直接维护有效的最小可转移状态。

    O(N)

    3.图像分析:

    如果画成图像,实际上可以看出这是一个区间为[i+1->i+p[j]-1]的阶梯递增函数,而每个阶梯的状态都由其上一个阶梯中prime最大值决定,所以端点值由上端转移而来,找到这个f[y]=f[x]+1.

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int INF=1e9;
     4 const int N=1e5+10;
     5 const int M=1e7+10;
     6 int n,c;
     7 int p[N];
     8 int child[N],f[M],q[M],num[M];
     9 int maxn; 
    10 int main(){
    11     cin>>n>>c;
    12     for(int i=1;i<=n;i++){
    13         scanf("%d",&p[i]);
    14     }    
    15     for(int i=1;i<=c;i++){
    16         scanf("%d",&child[i]);
    17         maxn=max(maxn,child[i]);
    18     }
    19     for(int i=1;i<=maxn;i++){
    20         f[i]=num[i]=INF;
    21     }
    22     for(int i=1;i<=n;i++){
    23         for(int j=p[i];j<=maxn;j+=p[i]){
    24             num[j]=p[i];
    25         }
    26     }  
    27     int l=1,r=2,st=1;q[1]=0;f[0]=0;num[0]=p[n];
    28     while(l<r&&st<=maxn){//队列维护 
    29         int x=q[l];l++;
    30         if(num[x]==INF) continue;
    31         int tmp=min(x+num[x]-1,maxn);
    32         for(;st<=tmp;st++){
    33             f[st]=f[x]+1;
    34             q[r++]=st;
    35         }
    36     }
    37     for(int i=1;i<=c;i++)
    38       {
    39             if(f[child[i]]==INF)printf("oo\n");
    40         else printf("%d\n",f[child[i]]);
    41       }
    42       return 0;
    43 }

    ——抓住了时间,却不会利用的人,终究也逃不过失败的命运。
  • 相关阅读:
    VBA基础四:数据库链接(WPS2019)
    VBA基础三:循环(DO...LOOP,)
    随机多人红包
    概率抽奖
    七步轻松实现大数据库表的数据转储
    SQL Server中的行列倒置技巧
    把对应表的字段跨表赋值
    sql内日期格式化输出
    事务的用法
    女孩,你为什么不沉住气奋斗
  • 原文地址:https://www.cnblogs.com/Nelson992770019/p/11153346.html
Copyright © 2011-2022 走看看