跳转至

2021-06-05-6月5日题解

6月5日题解

交换

题目链接

http://icpc.upc.edu.cn/problem.php?cid=2828&pid=9

题目描述

给出一个序列A,其中第i个数字为ai,你每次可以选择一个数字不变,将其他数字全部乘以x。其中x为任意素数。 无需考虑这些数字在变换过程中是否超过long long的存储范围。请回答:最少经过多少次操作,可以使得序列中所有数字全部相同。

输入

第一行包含一个正整数n,代表序列长度。 接下来一行包含n个正整数,描述序列中的每一个元素。

输出

输出一行一个正整数表示答案。

样例输入
2 
5 7
样例输出
2
提示

样例说明: 可以选中第二个数字不变,将第一个数字除以5,然后选中第一个数字不变,将第二个数字除以7。两次操作后,数组中所有数字均变为1。当然还有其他方法,如将两个数字最终都变为35也只需要2次操作。 【数据范围】 对于20%的数据,满足n=2,ai≤106 对于40%的数据,满足n≤10,ai≤106 对于另外20%的数据,满足n≤4∗104,ai≤20 对于100%的数据,满足1≤n≤106,1≤ai≤106

题解

对其他数进行做乘法相当于对自己做乘法,可以对每一个数除掉最大公约数互质后,统计和计算质因数的个数即可。

代码
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1005000]={0};
ll gcd(ll a,ll b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}
ll prime[1005000]={0};
ll is_prime[1005000]={0};
int main()
{
    ll cnt=0;
    for(ll i=2;i<=1e6;i++)
    {
        if(!is_prime[i])
            prime[++cnt]=i;
        for(ll j=1;j<=cnt;j++)
        {
            if(i*prime[j]>1e6)
                break;
            is_prime[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
    ll n,m=0;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        m=gcd(a[i],m);
    }
    ll ans=0;
    for(ll i=1;i<=n;i++)
    {
        a[i]/=m;
        for(ll j=1;j<=cnt&&prime[j]*prime[j]<=a[i];j++)
        {
            while(a[i]%prime[j]==0)
            {
                ans++;
                a[i]/=prime[j];
            }
        }
        if(a[i]!=1)
            ans++;
    }
    printf("%lld\n",ans);
}

Disjoint Set of Common Divisors

题目链接

http://icpc.upc.edu.cn/problem.php?cid=2826&pid=4

题目描述

Given are positive integers A and B. Let us choose some number of positive common divisors of A and B. Here, any two of the chosen divisors must be coprime. At most, how many divisors can we choose? →Definition of common divisor ·An integer d is said to be a common divisor of integers x and y when d divides both x and y. →Definition of being coprime ·Integers x and y are said to be coprime when x and y have no positive common divisors other than 1. →Definition of dividing ·An integer x is said to divide another integer y when there exists an integer α such that y=αx.

Constraints ·All values in input are integers. ·1≤A,B≤1012

输入

Input is given from Standard Input in the following format:

A B

输出

Print the maximum number of divisors that can be chosen to satisfy the condition.

样例输入
【样例1】
12 18
【样例2】
420 660
【样例3】
1 2019
样例输出
【样例1】
3
【样例2】
4
【样例3】
1
提示

样例1解释 12 and 18 have the following positive common divisors: 1, 2, 3, and 6. 1 and 2 are coprime, 2 and 3 are coprime, and 3 and 1 are coprime, so we can choose 1, 2, and 3, which achieve the maximum result. 样例3解释 1 and 2019 have no positive common divisors other than 1.

题解

求gcd(A,B)的质因数有多少个即可。

代码
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ll n,m;
    cin>>n>>m;
    ll k=__gcd(n,m);
    ll ans=1;
    for(ll i=2; i*i<=k; i++)
    {
        if(k%i==0)
            ans++;
        while(k%i==0)
            k/=i;
    }
    if(k!=1)
        ans++;
    cout<<ans<<endl;
    return 0;
}

牛牛的滑动窗口

题目链接

http://icpc.upc.edu.cn/problem.php?cid=2826&pid=9

题目描述

牛牛最近学习了滑动窗口类的算法,滑动窗口算法可以解决一些线性数组的离线静态区间查询类问题。 具体来说,假设对于一个数组进行m次静态区间查询问题。如果这些查询满足条件:∀i,j当li≤lj时,总有ri≤rj。(i,j表示查询的编号,l,r表示查询的左右端点) 接下来只要查询的问题满足可以快速插入和删除单点,就可以使用滑动窗口优化,将这m次查询的复杂度降低到O(n)。 显然,如果对于一个数组的区间查询问题,查询的区间长度给定为k时,总是满足∀i,j当li ≤ lj时,总有ri≤rj这一条件的。 牛牛接下来想要问你的问题也和定长滑动窗口有关。 众所周知,长度为k的滑动窗口从左到右去截取一个长度大小为n的数组时,一共可以截取到n-k+1个子数组。 牛牛将这n-k+1个子数组的极大值与极小值的乘积求和称为该数组的"第k窗口值"。 img 举个例子,假设长度为5的数组为[1,5,2,4,3],长度为3的滑动窗口可以截取三个子数组,它们分别为[1,5,2],[5,2,4],[2,4,3]。 所以该数组的“第3窗口值”为1*5+2*5+2*4=23。 对于一个给定大小的数组n,牛牛现在想要知道它的第1,2,3,4,5...n窗口值各是多少,请你编写程序解决他的问题。

输入

第一行输入一个正整数n,表示数组的大小。 接下来一行输入n个正整数ai,表示数组的内容

输出

输出一行n个正整数,表示滑动窗口的长度分别为1,2,3,4,5...n时,问题的答案。 输出的整数之间用空格隔开,行末不允许有多余空格。

样例输入
5 
1 5 2 4 3
样例输出
55 35 23 15 5
提示

样例解释: 第1窗口值=1*1+5*5+2*2+4*4+3*3=55 第2窗口值=5*1+5*2+4*2+4*3=35 第3窗口值=5*1+5*2+4*2=23 第4窗口值=5*1+5*2=15 第5窗口值=5*1=5

对于10%的测试数据,保证1≤n≤10 对于20%的测试数据,保证1≤n≤100 对于30%的测试数据,保证1≤n≤1000 对于50%的测试数据,保证1≤n≤6000 对于另外10%的测试数据,保证1≤ai≤10 对于100%的测试数据,保证1≤n≤105,1≤ai≤100

题解

暂未想出,思路为单调栈,待补!!!

https://blog.csdn.net/weixin_43346722/article/details/109151074

代码
#include<cstdio>
#include<iostream>

using namespace std;

struct node {
    int x, num;
}bigstack[101], smallstack[101];
int n, a[100001], ans[100001], bigtop, smalltop, maxn, minn;
int lft, bignow, smallnow;

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i++) {
        while (bigtop && bigstack[bigtop].x <= a[i]) bigtop--;
        while (smalltop && smallstack[smalltop].x >= a[i]) smalltop--;//栈的弹出

        bigstack[++bigtop] = (node){a[i], i};//插入
        smallstack[++smalltop] = (node){a[i], i};
        maxn = minn = a[i];
        lft = i;
        bignow = bigtop - 1;
        smallnow = smalltop - 1;

        while (bignow || smallnow) {//一点一点往后移,算贡献
            if (bignow && smallnow) {
                if (bigstack[bignow].num >= smallstack[smallnow].num) {
                    ans[i - lft + 1] += maxn * minn;
                    ans[i - bigstack[bignow].num + 1] -= maxn * minn;
                    lft = bigstack[bignow].num;
                    maxn = bigstack[bignow].x;
                    bignow--;
                }
                else {
                    ans[i - lft + 1] += maxn * minn;
                    ans[i - smallstack[smallnow].num + 1] -= maxn * minn;
                    lft = smallstack[smallnow].num;
                    minn = smallstack[smallnow].x;
                    smallnow--;
                }

                continue;
            }

            if (bignow) {
                ans[i - lft + 1] += maxn * minn;
                ans[i - bigstack[bignow].num + 1] -= maxn * minn;
                lft = bigstack[bignow].num;
                maxn = bigstack[bignow].x;
                bignow--;
            }

            else {
                ans[i - lft + 1] += maxn * minn;
                ans[i - smallstack[smallnow].num + 1] -= maxn * minn;
                lft = smallstack[smallnow].num;
                minn = smallstack[smallnow].x;
                smallnow--;
            }
        }

        ans[i - lft + 1] += maxn * minn;
        ans[i + 1] -= maxn * minn;
    }

    for (int i = 1; i <= n; i++) {
        ans[i] += ans[i - 1];//因为是差分,所以要加上前面的
        printf("%d ", ans[i]);
    }

    return 0;
}

等差序列前缀和(双前缀和)

听说罗翔老师最近很火。 张三今天迫不及待想犯罪。就决定是爆炸罪了,正好罗老师讲了这个案例。 他一眼就看上了 CaPeF_Yyx 的家 (或许是他太 duliu 了) 。 作为张三,她蛮不讲理,所以他的爆炸会波及部分街道。每次爆炸会造成一定的损坏。 作为张三,她出人意料,所以他的爆炸波及的范围每次都不一样。 作为张三,她十恶不赦,所以他的爆炸会进行m 次 (CaPeF_Yyx 的家也太坚固了吧) 作为张三,她井井有条,所以他的爆炸波及的范围是一个闭区间。 作为张三,她精益求精,所以他的爆炸以等差数列的方式造成伤害。 CaPeF_Yyx 家在偏远角落,街道编号从1~n 。 CaPeF_Yyx 街道刚翻新,每间房屋破坏程度都为0。 罗翔老师可以预测每次爆炸的范围。会给到 CaPeF_Yyx 形如[l,r] 的闭区间。 罗翔老师可以预测每次爆炸的伤害。会给到 CaPeF_Yyx 形如l,r,bg,ed 的 4 个数。 表示形如{ql,ql+1,...,qr-1,qr},ql=bg,qr=ed的等差数列,对于街道ai(l≤i≤r),造成qi的伤害。 CaPeF_Yyx 找到了 Rainy7 修复街道。可是她必须先知道街道到底损坏了多少。 她还忙着拯救世界呢,CaPeF_Yyx 被吓得不轻,于是只能让你帮助她了。 出题人不想太 duliu ,她让你只要输出所有房屋损害程度的最大值和异或和。 Q:话说为什么不直接用魔法阻止张三? A:因为 唯我法外狂徒张三永世长存 !!!

输入

第一行,两个整数n,m表示房屋的数量,和爆炸次数。

接下来m行,每行四个整数l,r,bg,ed,分别表示等差数列的首项标号,末项标号,首项值,末项值。

输出

一行,两个数,分别表示所有房屋损害程度的最大值和异或和。

样例输入
【样例1】
5 2
1 5 2 10
2 4 1 1

【样例2】
6 2
1 5 2 10
2 4 1 1
样例输出
【样例1】
3 10

【样例2】
3 10
提示

样例1解释: 第一次爆炸产生的伤害:[2,4,6,8,10]。 第二次爆炸产生的伤害:[0,1,1,1,0]。 所有爆炸结束后每个房屋的损伤程度:[2,5,7,9,10]。 输出异或和:img。 输出最大值:imgimg

代码
#include <bits/stdc++.h>
using namespace std;
const long long int N = 1e7 + 500;
long long int out[N] = {0};
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    register long long int n, m, l, r, ks, e1, dc, tmp = 0;
    register long long int max1 = 0, ans = 0;
    cin >> n >> m;
    register long long int i;
    for (i = 1; i <= m; i++)
    {
        cin >> l >> r >> ks >> e1;
        dc = (e1 - ks) / (r - l);
        out[l] = out[l] + ks;
        out[l + 1] = out[l + 1] + dc - ks;
        out[r + 1] = out[r + 1] - dc - e1;
        out[r + 2] = out[r + 2] + e1;
    }
    for (i = 1; i <= n; i++)
    {
        out[i] = out[i - 1] + out[i];
        tmp += out[i];
        max1 = max(max1, tmp);
        ans = ans ^ tmp;
    }
    cout << ans << ' ' << max1 << endl;
    return 0;
}