Educational Codeforces Round 14 - F (codeforces 691F)

题目链接:http://codeforces.com/problemset/problem/691/F

题目大意:给定n个数,再给m个询问,每个询问给一个p,求n个数中有多少对数的乘积≥p

数据范围:2≤n≤10^6, 1≤ai≤3*10^6,1≤m≤10^6, 1≤p≤3*10^6

解题思路:比赛的时候比较naive的思路是把n中的数字排序去了重之后,对于每个p,最多枚举√p步,就能得到答案。而这个naive的思路是O(p√p)的,结果T了。

后来百思不得其解,去看了官方的解答。感觉是一种很有必要总结的思路。思路的模型是埃氏筛素数。

筛素数中枚举到一个素数pr,我们就把MAX范围内的pr的倍数依次搭上标记。这样做看似暴力,实际上复杂度近乎O(n)(其实是O(MAX/p1+MAX/p2..MAX/pk))

回到这道题目,完全可以借鉴上述思路,从1-MAX枚举a,再从1-MAX/a枚举另一半b,记n个数中乘积等于i的对数ans[i],那么就有ans[a*b] += cnt[a]*cnt[b];其中cnt[i]表示n个数中i这个数出现了多少次。仔细分析复杂度的话是O(MAX/1+MAX/2+MAX/3+...+MAX/MAX),事实上,这个东西是接近O(MAXlogMAX)的,这种思想在处理一些数论问题中同样很有借鉴意义。我认为这种思路复杂度的支撑点在于他有一个上限MAX,并且内部的操作是相乘。

最后得到了ans[i]之后取个前缀和就得到了n个数中乘积<p的对数,用总对数一减就得到了答案。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;

const int MaxN = 3e6;
int n, m, MaX;
int a[MaxN + 5], p[MaxN + 5];
LL cnt[MaxN + 5], sum[MaxN + 5];

int main()
{
    while (~scanf("%d", &n)) {
        memset(cnt, 0, sizeof(cnt));
        memset(sum, 0, sizeof(sum));
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]), cnt[a[i]]++;
        scanf("%d", &m); MaX = -(1 << 30);
        for (int i = 1; i <= m; i++)
            scanf("%d", &p[i]), MaX = max(MaX, p[i]);
        for (int i = 1; i <= MaX; i++)
            for (int j = 1; i * j <= MaX; j++)
                if (i != j) sum[i * j] += cnt[i] * cnt[j];
                    else sum[i * j] += cnt[i] * cnt[i] - cnt[i];
        for (int i = 1; i <= MaX; i++) sum[i] += sum[i - 1];
        for (int i = 1; i <= m; i++)
            printf("%I64d\n", (LL)n * (n - 1) - sum[p[i] - 1]);
    }
}

时间: 2024-10-18 05:03:16

Educational Codeforces Round 14 - F (codeforces 691F)的相关文章

Educational Codeforces Round 23 F. MEX Queries(线段树)

题目链接:Educational Codeforces Round 23 F. MEX Queries 题意: 一共有n个操作. 1.  将[l,r]区间的数标记为1. 2.  将[l,r]区间的数标记为0. 3.  将[l,r]区间取反. 对每个操作,输出标记为0的最小正整数. 题解: hash后,用线段树xjb标记一下就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<&l

Educational Codeforces Round 25 F. String Compression(kmp+dp)

题目链接:Educational Codeforces Round 25 F. String Compression 题意: 给你一个字符串,让你压缩,问压缩后最小的长度是多少. 压缩的形式为x(...)x(...)  x表示(...)这个出现的次数. 题解: 考虑dp[i]表示前i个字符压缩后的最小长度. 转移方程解释看代码,这里要用到kmp来找最小的循环节. 当然还有一种找循环节的方式就是预处理lcp,然后通过枚举循环节的方式. 这里我用的kmp找的循环节.复杂度严格n2. 1 #inclu

Educational Codeforces Round 24 F. Level Generation(三分)

题目链接:Educational Codeforces Round 24 F. Level Generation 题意: 给你n个点,让你构造ans条边,使得这ans条边中至少有一半是桥. 让你求ans的最大值. 题解: 首先我们将每一个点按顺序连起来,那么可以构成n-1个桥. 然后我们可以把其中的x个点拿出来连边,这些边都不是桥. x个点最多能连x*(x-1)条边,然后剩下的n-x个点连的边将会构成桥. 然后就可以构造一个函数关系,详见check函数,然后三分一下就行了. 1 #include

Educational Codeforces Round 21 F. Card Game(网络流之最大点权独立集)

题目链接:Educational Codeforces Round 21 F. Card Game 题意: 有n个卡片,每个卡片有三个值:p,c,l; 现在让你找一个最小的L,使得满足选出来的卡片l<=L,并且所有卡片的p的和不小于k. 选择卡片时有限制,任意两张卡片的c之和不能为质数. 题解: 和hdu 1565 方格取数(2)一样,都是求最大点权独立集. 不难看出来,这题再多一个二分. 注意的是在构造二部图的时候,按照c值的奇偶性构造. 当c==1时要单独处理,因为如果有多个c==1的卡片,

Educational Codeforces Round 76 F 折半枚举

Educational Codeforces Round 76 F 折半枚举 https://codeforces.com/contest/1257/problem/F 题意: 数组a,找到一个x使得a中每一个元素异或x后"二进制中1的个数"相同. 数组长度100,数字大小2^30. 思路: 折半枚举答案X,如分为X前15位和后15位.之后我们再枚举期望的"相同个数"是多少,通过hash看看能不能满足就好了. 代码: #include <bits/stdc++

Educational Codeforces Round 14 D. Swaps in Permutation (并查集orDFS)

题目链接:http://codeforces.com/problemset/problem/691/D 给你n个数,各不相同,范围是1到n.然后是m行数a和b,表示下标为a的数和下标为b的数可以交换无数次.问你最后字典序最大的数列是什么. 将下面的a和b用并查集联系起来存到祖节点对应的数组中,然后从大到小排序数组,最后依次按照父节点的数组中的顺序输出. 也可以用dfs的方法解(略麻烦),形成一个环路的就在一个数组中... 1 //并查集 2 #include <bits/stdc++.h> 3

Educational Codeforces Round 28 F. Random Query

题意:一个数列,随机选l,r,f(l,r)为l,r区间内数的种数,问f(l,r)的期望 思路:sum(每个数算出他的贡献)/(n*n),我们这只考虑l<=r ,对于当前这数字他能贡献后面的所有区间,但是对于前面的话,他只共贡献到前一个相同的数后面 比如  1  2  3  4  2  5  6 对于第一个2  他贡献于  (1,2) (1,3)(1,4)(1,5)(1,6)(1,7) (2,2) (2,3)(2,4)(2,5)(2,6)(2,7) 对于第2个2  贡献于     (3,5)(3,

Educational Codeforces Round 72 F. Forced Online Queries Problem

题目梗概 有一张\(n\)个点的图,刚开始没有边,现在又两种操作,一种是加入一条边(如果这条边存在,否则删去这条边),一种是询问\(x,y\)是否联通. \(x,y\)给出的形式是\((x+last-1)%n+1\),\((y+last-1)%n+1\),\(last\)为上一次询问的答案. 解题思路 对于这题的离线版本有两种写法:线段树分治,以及对操作分块. 线段树分治的做法比较常见,下面来讲讲分块的做法. 对于当前块之前的操作,我们可以找出哪些边在当前块需要操作,设为集合\(B\),当然就有

Educational Codeforces Round 83 --- F. AND Segments

解法 根据题意,就是拆位,每一位上有两种限制,在[l,r]上全1或者存在0. 那我们考虑对每一位单独操作,f[i][j]表示处理了前i个位置,上次出现0是在j位置. 对于每一个可以放0的位置,\(f[i][i]=\sum_{j=1}^{i-1} f[i-1][j]\) 对于每个位置r,如果存在限制[l,r]存在0,那么\(f[r][1\sim l-1]\)就都会remove成0,成为非法情况 最后每位\(Ans=\sum_{i=0}^n f[n][i]\) 如果暴力做的话每一位是\(n^{2}\