2020牛客多校第九场 解题报告(AEFIK)

题目链接:https://ac.nowcoder.com/acm/contest/5674#question

A-Groundhog and 2-Power Representation

题意

  • 求表达式的值
  • 只有2 0 + ( ) 组成
  • 2(0)表示2的0次

思路

  • 用python写非常方便
  • 写个x(i)函数表示2的幂次,然后将字符串中的"2("字符替换成"x("
  • 最后调用eval函数将字符串变成有效的表达式求值并返回结果

AC代码

def x(i):
    return 2**i
str = input()
str = str.replace("2(","x(")
print(eval(str))

E-Groundhog Chasing Death

题意

  • \prod_{i=a}^{b}\prod_{j=c}^{d}gcd(x^i,y^j)mod 998244353的结果

思路

  • 唯一分解定理:任何数都可以表示成质因子幂次相乘的形式。n=p_1^{a_1}*p_2^{a_2}...*p_k^{a_k},m=p_1^{b_1}*p_2^{b_2}...*p_k^{b_k}
  • gcd(n,m)=p_1^{min(a_1,b_1)}*p_2^{min(a_2,b_2)}...*p_k^{min(a_k,b_k)}
  • 因为a^n*a^m=a^{n+m},所以设p是n,m共有的质因子,k1和k2是n,m对于p的幂次,则就是求解\prod p^{\sum_{i=a}^{b}\sum_{j=c}^{d}min(k1*i,k2*j)}(p是n,m共有的质因子)
  • 优化一个for,找到k1*i和k2*j的零界点sh,sh=k1*i/k2,此时j在sh左边的时候,min总是取k1*i,由于此时的i不变,直接乘数量就好了;j在sh右边的时候,min总是取k2*j,由于j在变,不过就是对等差数列求和。
  • 数规模会很大,取模太多又感觉很烦,其他人是通过欧拉降幂优化,我门就很莽,直接__int128冲,最后取模!!

AC代码

#include 
#define LL long long
#define sc(a) scanf("%d", &a)
#define sc2(a, b) scanf("%d%d", &a, &b)
#define sc3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define scl(a) scanf("%lld", &a)
#define scl2(a, b) scanf("%lld%lld", &a, &b)
#define ss(a) scanf("%s", a)
#define mem(a, b) memset(a, b, sizeof(a))
#define PII pair
#define ll __int128
using namespace std;
const ll mod = 998244353;
inline void print(__int128 x)
{
    if (x < 0)
    {
        x = -x;
        putchar('-');
    }
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}
template 
inline void read(_Tp &x)
{
    char ch;
    bool flag = 0;
    x = 0;
    while (ch = getchar(), !isdigit(ch))
        if (ch == '-')
            flag = 1;
    while (isdigit(ch))
        x = x * 10 + ch - '0', ch = getchar();
    if (flag)
        x = -x;
}
inline ll POW(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
map num1; //存放x质因子和需要幂的数ai
map num2; //存放y质因子和需要幂的数bi
map ans;  //共同质因子和需要幂的数
int main()
{
    ll a, b, c, d, x, y;
    read(a);
    read(b);
    read(c);
    read(d);
    read(x);
    read(y);

    for (ll i = 2; i * i <= x; i++) //x质因子分解
        if (x % i == 0)
        {
            int cnt = 0;
            while (x % i == 0)
                cnt++, x /= i;
            num1[i] = cnt;
        }
    if (x != 1)
        num1[x] = 1;

    for (ll i = 2; i * i <= y; i++) //y质因子分解
        if (y % i == 0)
        {
            int cnt = 0;
            while (y % i == 0)
                cnt++, y /= i;
            num2[i] = cnt;
        }
    if (y != 1)
        num2[y] = 1;
    for (auto p : num1)
    {
        if (!num2[p.first])
            continue;
        ll pi = p.first, k1 = p.second, k2 = num2[pi];
        for (ll i = a; i <= b; i++)
        {
            ll sh = k1 * i / k2; //j从1到sh,min都是k1*i;
                                 //j大于sh,min都是k2*j
            if (sh < c)
                ans[pi] += k1 * i * (d - c + 1);
            else if (sh >= d)
                ans[pi] += k2 * (c + d) * (d - c + 1) / 2; //等差数列求和
            else
                ans[pi] += k1 * i * (d - sh) + k2 * (sh + c) * (sh - c + 1) / 2;
        }
    }
    ll res = 1;
    for (auto p : ans) //所有共同质因子幂次累乘
        res = res * POW(p.first, p.second) % mod;
    print(res % mod);
    system("pause");
    return 0;
}

F-Groundhog Looking Dowdy 

题意

  • 给定n天,每天生产ki件衣服,选择m件来自不同天的衣服,求最大价格和最小价格的最小差值
  • 数据范围:1<=aij<=1e9,1<=n<=1e6,1<=m<=n,sum of clothes<=2e6,k>=1

思路

  • 尺取法。 
  • 首先结构体存衣服生产日期和价格,按照价格从小到大排序。
  • l,r分别是左右端点,当区间内衣服不同生产日期数num没有超过m时,r++;
  • 等于m时,更新左右端点价格差值的最小值 ,l++,直到num

AC代码

#include 
#define LL long long
#define sc(a) scanf("%d", &a)
#define sc2(a, b) scanf("%d%d", &a, &b)
#define sc3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define scl(a) scanf("%lld", &a)
#define scl2(a, b) scanf("%lld%lld", &a, &b)
#define ss(a) scanf("%s", a)
#define mem(a, b) memset(a, b, sizeof(a))
#define PII pair
using namespace std;
const double pi = acos(-1.0);
const int mod = 1e9 + 7;
const int maxn = 1e6+5;
const int inf = 0x3f3f3f3f;
struct node{
    int val,id;
    bool operator < (const node &a)const{
        return val

由于数据给的弱,以下代码可以水过去,要不是敲得慢,差点拿一血QAQ

#include 
#define LL long long
#define sc(a) scanf("%d", &a)
#define sc2(a, b) scanf("%d%d", &a, &b)
#define sc3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define scl(a) scanf("%lld", &a)
#define scl2(a, b) scanf("%lld%lld", &a, &b)
#define ss(a) scanf("%s", a)
#define mem(a, b) memset(a, b, sizeof(a))
#define PII pair
using namespace std;
const int maxn = 2e5+5;
const int inf = 0x3f3f3f3f;
int main(){
    int n,m;
    sc2(n,m);
    vectorv;
    while(n--){
        int k,x,mii=inf;sc(k);
        while(k--){
            sc(x);
            mii=min(mii,x);
        }
        v.push_back(mii);//每天取最便宜的
    }
    sort(v.begin(),v.end());//从小到大排
    cout<

I-The Crime-solving Plan of Groundhog 

题意

  • 给一个序列仅包含数字,让你将其打乱次序并组合成两部分,使这两部分组成的数字相乘得到的结果最小
  • 不允许任何一部分组成的数字包含前导零

思路

  • 大数运算。 两位乘两位的数字一定比一位乘三位的数字大。证明过程如下:
  • (10a+d)*(10b+c)=100ab+10ac+10bd+cd
  • (100b+10c+d)*a=100ab+10ac+ad<(10a+d)*(10b+c)
  • 组合成1位数乘n-1位数
  • 1位数是除零外最小的
  • n-1位数是第一位是除零最小,接着是从小到大加入
  • 然后是模拟乘法运算。

AC代码

#include 
#define LL long long
#define sc(a) scanf("%d", &a)
#define sc2(a, b) scanf("%d%d", &a, &b)
#define sc3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define scl(a) scanf("%lld", &a)
#define scl2(a, b) scanf("%lld%lld", &a, &b)
#define ss(a) scanf("%s", a)
#define mem(a, b) memset(a, b, sizeof(a))
#define PII pair
using namespace std;
const int maxn = 1e5+5;
int cnt[maxn],a[maxn];
int main(){
    int t;sc(t);
    while(t--){
        int n,x;sc(n);
        mem(cnt,0);//数组清零
        for(int i=1;i<=n;i++){
            sc(x);
            cnt[x]++;
        }
        int bas=0,ca=0;
        for(int i=1;i<10;i++){//找第一个非零
            if(cnt[i]){
                bas=i;
                cnt[i]--;
                break;
            }
        }
        for(int i=1;i<10;i++){//找第一个非零
            if(cnt[i]){
                a[++ca]=i;
                cnt[i]--;
                break;
            }
        }
        for(int i=0;i<10;i++){//从小到大加入
            if(cnt[i]){
                while(cnt[i]--){
                    a[++ca]=i;
                }
            }
        }
        string b;
        for(int i=ca;i>=1;i--){//从个位开始乘
            t+=bas*a[i];
            b+=t%10+'0';//存余数
            t/=10;      //进位
        }
        if(t)b+=t+'0';  //最后进位
        reverse(b.begin(),b.end());//字符串反转
        cout<

K-The Flee Plan of Groundhog

题意

  • 有n个结点,n-1条边,小A从1号结点出发前往n号结点看望小B,每秒能走一条路
  • t秒钟之后小B从n结点出发,来追小A,每秒能走两条路
  • 问最迟要多久才能相遇

思路

  • 以n结点为根dfs递归建树。pre数组存放父节点,dep数组存放结点的深度,maxd数组存放子节点最深深度。
  • n个结点,n-1条边,说明这是一棵树,所以小A从1出发去n只有一条路径,t秒之后小A的位置可以计算出来 。
  • 求的是最迟相遇时间,那么小A就要远离小B,往树的深处跑,那么就有两种情况。
  • 第一种情况,小A还没到树的最深处就被小B追上了。因为小B速度是小A的两倍,那么追逐时间其实就是两人的深度之差。
  • 第二种情况,小A到最深处等小B。那么追逐时间就是小B到达最深处的时间。

AC代码

#include 
#define LL long long
#define sc(a) scanf("%d", &a)
#define sc2(a, b) scanf("%d%d", &a, &b)
#define sc3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define scl(a) scanf("%lld", &a)
#define scl2(a, b) scanf("%lld%lld", &a, &b)
#define ss(a) scanf("%s", a)
#define mem(a, b) memset(a, b, sizeof(a))
#define PII pair
using namespace std;
const int maxn = 1e5+5;
vectorv[maxn];
int pre[maxn],dep[maxn],maxd[maxn];
void add(int a,int b){
    v[a].push_back(b);
    v[b].push_back(a);
}
void dfs(int s,int fa){             //dfs建树
    for(auto i:v[s]){               //遍历子树
        if(i==fa)continue;          //不必跑父节点
        pre[i]=s;                   //存放父节点
        dep[i]=dep[s]+1;            //子节点的深度等于父节点深度加1
        maxd[i]=dep[i];             //存放子节点最深深度
        dfs(i,s);                   //向下递归
        maxd[s]=max(maxd[s],maxd[i]);//递归到根节点时,从下到上更新子节点最深深度
    }
}
int main(){
    int n,t;
    sc2(n,t);
    for(int i=1;i

 

你可能感兴趣的:(牛客多校)