2019 HL暑假集训 Day5

题目按难度从简到难手动排序————题记

T1 :最大公约数(gcd.c/cpp/pas)

【 题目描述】:

给定 n n n 个数, 从中选出 K K K 个。
A l i c e Alice Alice 想让 K K K 个数的最大公约数尽可能大, 求最大的最大公约数。

【输入数据】:

第一行两个正整数 n , K n, K n,K
第二行 n n n 个正整数, 即给定的 n n n 个数。

【输出数据】:

输出一个正整数表示最大的最大公约数。
【样例输入】
3 1
1 2 3

【样例输出】:

3

【数据范围】:

对于 30 30 30%的数据, n < = 20 n <= 20 n<=20
对于 60 60 60%的数据, 保证输入中所有数小于 5000 5000 5000
对于 100 100 100%的数据, 保证输入中所有数小于 5 ∗ 1 0 5 5*10^{5} 5105, K < = n K <= n K<=n


心路历程:一拿到这道题,一眼看出30分的方法是用 d f s dfs dfs枚举选数方案,突然想到前几天做的一道题(见算法竞赛进阶指南Interval GCD),里面有个操作是询问 [ l , r ] [l,r] [l,r]区间中的最大公约数,上面用的方法是求差分序列,我感觉可以借鉴,于是开始想差分序列,以失败告终,突然想到二分答案,貌似可行,复杂度是 O ( n l o g n ) O(n log n) O(nlogn),于是开始打代码,一遍过了大样例,心里感觉很稳,但是突然意识到这道题的答案并可以二分,举个例栗子(现在二分到 3 3 3,发现 3 3 3不是符合题意的 g c d gcd gcd值,就会向 < = 3 <=3 <=3的区间搜索,但是可能真正的 g c d gcd gcd值是一个 > 3 >3 >3的值),舍不得白写,就加了随机化,当 n < = 20 n<=20 n<=20时跑 d f s dfs dfs,当 n > = 20 n>=20 n>=20时跑二分答案,然后看下一道了。

正解:开桶存下每个权值出现了几次,然后枚举 g c d gcd gcd值。 枚举它的所有倍数, 看出现次数是否大于等于 k k k 就可以了。 复杂度 O ( n + 2 n + 3 n + … + n n ) = O ( n l n n ) ≈ O ( n l o g n ) O(n+\frac {2} {n}+\frac {3} {n}+…+\frac {n} {n})=O(nlnn)≈O(nlogn) O(n+n2+n3++nn)=O(nlnn)O(nlogn),证明看“调和级数求和”。


正解代码:
#include <bits/stdc++.h>
using namespace std;

const int N = 600001;
int n, m, k;
int b[N], a[N];
int Max = -1, maxx = -1;
int vis[N] = {};

int main() {
	int lx = 0;
	freopen("gcd.in","r",stdin);
	freopen("gcd.out","w",stdout);
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]), maxx = max(maxx, a[i]), b[a[i]]++;
	for (int i=maxx;i>=1;i--) {
		int ans = 0;
		for (int j=i;j<=maxx;j=j+i)
			ans += b[j];
		if (ans >= k) {
			printf("%d",i);
			return 0;
		}
	}
	return 0;
}

T2.计数(count.c/cpp/pas)

【题目描述】:

A l i c e Alice Alice B o b Bob Bob 在平面直角坐标系中下棋。 A l i c e Alice Alice 的棋子初始时在 ( 0 , 0 ) (0, 0) (0,0)位置, 要走到 ( a , b ) (a, b) (a,b)
置; B o b Bob Bob的棋子初始时在 ( c , 0 ) (c, 0) (c,0)位置, 要走到 ( a , d ) (a, d) (a,d)位置。棋子只能沿 x x x轴或 y y y 轴正方向移动若
干个单位长度, 问有多少种移动方案使两颗棋子的移动路径不相交。
【输入数据】
输入一行 4 个正整数, 依次为 a , b , c , d a, b, c, d a,b,c,d

【输出数据】:

输出总方案数对质数 100000007 取模的结果。

【样例输入】:

3 2 1 1

【样例输出】:

6

【样例解释】:

A 走 ( 0 , 0 ) → ( 0 , 2 ) → ( 3 , 2 (0,0) → (0,2) → (3,2 (0,0)(0,2)(3,2) 时, B 有 3 种走法:
( 1 , 0 ) → ( 1 , 1 ) → ( 3 , 1 ) (1,0) → (1,1) → (3,1) (1,0)(1,1)(3,1)
( 1 , 0 ) → ( 2 , 0 ) → ( 2 , 1 ) → ( 3 , 1 ) (1,0) → (2,0) → (2,1) → (3,1) (1,0)(2,0)(2,1)(3,1)
( 1 , 0 ) → ( 3 , 0 ) → ( 3 , 1 ) (1,0) → (3,0) → (3,1) (1,0)(3,0)(3,1)
A 走 ( 0 , 0 ) → ( 0 , 1 ) → ( 1 , 1 ) → ( 1 , 2 ) → ( 3 , 2 (0,0) → (0,1) → (1,1) → (1,2) → (3,2 (0,0)(0,1)(1,1)(1,2)(3,2)时, B 有 2 种走法。
A 走 ( 0 , 0 ) → ( 0 , 1 ) → ( 2 , 1 ) → ( 2 , 2 ) → ( 3 , 2 ) (0,0) → (0,1) → (2,1) → (2,2) → (3,2) (0,0)(0,1)(2,1)(2,2)(3,2)时, B 有 1 种走法。

【数据范围】:

对于 50%的数据, a + b < = 20 a + b <= 20 a+b<=20
对于 70%的数据, a + b < = 2 ∗ 1 0 4 a + b <= 2*10^4 a+b<=2104
对于 100%的数据, a + b < = 2 ∗ 1 0 5 a + b <= 2*10^5 a+b<=2105 a > c , b > d a > c, b > d a>c,b>d


心路历程: 拿到这道题,第一反应就是求方案数,但是没有好的思路去求这个方案数,于是想了10min后就将这道题放在一边,看下一题了。

正解
如果不考虑路径相交的问题,那么总共有 C a − c + d a − c ∗ C a + b a C_{a-c+d}^{a-c} * C_{a+b}^{a} Cac+dacCa+ba种方法。
现在问题是, 这些方案中,有多少是路径相交的呢?
注意到如果两条路径相交,可以看成 A , B A,B A,B 棋子交换目的地。
考虑从 ( 0 , 0 ) (0,0) 0,0走到 ( a , d ) (a,d) ad,那么一共有 C a + d a C_{a+d}^{a} Ca+da种走法;从 ( c , 0 ) (c,0) c,0走到 ( a , b ) (a,b) ab一共有 C a − c + d a − c C_{a-c+d}^{a-c} Cac+dac种走法。
相交的路径总共是 C a + d a ∗ C a − c + d a − c C_{a+d}^{a} * C_{a-c+d}^{a-c} Ca+daCac+dac种。
两式作差即为原题所求。
良心提醒:组合数的求解可以使用逆元,求逆元及逆元的使用方法请自行 https://www.baidu.com.

本题最大坑点:题目模数为 1 0 8 + 7 10^{8}+7 108+7,而不是 1 0 9 + 7 10^{9}+7 109+7


正解代码:
#include <bits/stdc++.h>
using namespace std;

const int N = 1110000;
const int Mod = 100000007;
int n, m;
long long c[N] = {};
long long a, b, cc, d;

inline long long Power(long long x,long long y) {
	long long ans = 1;
    while (y){
	    if (y & 1) ans = (ans * x) % Mod;
	    y >>= 1;
	    x = x * x % Mod;
	}
	return ans % Mod;
}

inline long long C(int x,int y) {
	if (x < y) swap(x, y);
	return c[x] * Power(c[x-y]*c[y]%Mod, Mod-2) % Mod;
}
int main() {
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%lld%lld%lld%lld",&a,&b,&cc,&d);
	long long sum = 1;
	c[1] = 1;
	for (int i=2;i<=500001;i++)
		c[i] = (c[i-1] * i) % Mod;
	sum = sum * C(a+b, a) * C(a-cc+d, a-cc) % Mod;
	sum = (sum - (C(a+d, a) * C(a-cc+b, a-cc)) % Mod + Mod) % Mod;
	printf("%lld",sum);
	return 0;
}

T3.异色弧(arc)

【题目描述】:

在数轴上有 n n n个点,它们的坐标分别为 ( 1 , 0 ) , ( 2 , 0 ) , … , ( n , 0 ) (1,0), (2,0), …, (n,0) (1,0),(2,0),,(n,0)。每个点都有一个颜色,坐标为
( i , 0 ) (i,0) (i,0)的点颜色为 a i a_i ai
A l i c e Alice Alice 在所有颜色相同的点对间都画上了圆弧。更具体地, 如果有 a i a_i ai = a j a_j aj i i i j j j, Alice
会画一条圆弧连接 ( i , 0 ) (i,0) (i,0) ( j , 0 ) (j,0) (j,0), 且这条弧颜色为 a i a_i ai,所有圆弧都在第一象限内。
A l i c e Alice Alice 想知道有多少对不同颜色的圆弧相交了。

【输入数据】:

第一行一个整数 n 表示点数。
第二行 n 个整数 ai 表示所有点的颜色。

【输出数据】:

一行一个整数表示答案,答案对质数 1 0 9 + 7 10^{9}+7 109+7 取模。

【样例输入】:

8 1
2 3 1 2 3 2 1

【样例输出】:

8

【数据范围】:

对于 20%的数据: 1 < = n < = 10 1 <= n <= 10 1<=n<=10
对于 40%的数据: 1 < = n < = 1000 1 <= n <= 1000 1<=n<=1000
另有 30%的数据, 每种颜色的点数不超过 20 20 20
对于 100%的数据: 1 < = n , a i < = 1 e 5 1 <= n, ai <= 1e5 1<=n,ai<=1e5


心路历程:题目很好懂,求相交数,20分的代码也很好写,直接暴力 d f s dfs dfs就行了,开始考虑40分做法,Nmin后发现,对于一个点对 i , j i,j i,j,将区间 [ i , j ] [i,j] [i,j]的贡献和区间 [ j + 1 , n ] [j+1,n] [j+1,n]的贡献相乘,就是 i , j i,j i,j的贡献,时间不多了,就没有深入考虑,直接敲代码,一遍过样例后就不管它了。果然,最后评测结果爆0了。

正解
考虑容斥,计算所有异色圆弧对数减去 A A B B AABB AABB 式与 A B B A ABBA ABBA
A A B B AABB AABB式容易计算,枚举第二个A,左右两边的贡献可以线性得出
A B B A ABBA ABBA式的情况比较复杂,考虑平衡规划的思想
根据颜色的出现次数 c n t i cnt_i cnti分类,假设以k为界
c n t A cnt_A cntA>= k k k c n t A cnt_A cntA < k k k c n t B cnt_B cntB >= k k k c n t A cnt_A cntA < k k k c n t B cnt_B cntB < k k k三种情况进行讨论
记prei_j表示前 i i i个点颜色 j j j的出现次数
P a r t 1 Part1 Part1 c n t A cnt_A cntA >= k k k
B B B的弧为 ( x , y ) (x, y) (x,y)则贡献为prex_A * ( c n t A − p r e y A ) (cnt_A - prey_A) (cntApreyA)
由于 A A A不超过 k n \frac {k} {n} nk种,因此我们枚举 A A A再枚举 B B B,在扫描颜色B的点的同时维护前缀和计算贡献,复杂度为 O ( n 2 k ) O( \frac{n^2}{k} ) O(kn2)
P a r t 3 Part3 Part3 c n t A cnt_A cntA < k k k c n t B cnt_B cntB >= k k k
与上面类似,但此时我们只能枚举颜色 B B B,列出计算式子后,仍然枚举颜色为 A A A的点,并计算和式的变化
P a r t 3 Part3 Part3 c n t A cnt_A cntA < k k k c n t B cnt_B cntB < k k k
类似一个二维数点,数据结构维护即可.

正解代码:Loding… … …
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值