暑假2019培训:Day6提高组测试赛

概述

又是一个接近yu暴零的比赛,说好的图论》》?????????????????????在这里插入图片描述
第一题作死,想看看运气,结果只有10分,第二题暴力50分,第三题暴力没有打出来……


题目顺序

  • 1.最大公约数
  • 2.计数
  • 3.异色弧

最大公约数(gcd)

【 题目描述】

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

【输入数据】

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

【输出数据】

输出一个正整数表示最大的最大公约数。

【样例输入】

3 1
1 2 3

【样例输出】

3

【数据范围】

对于 30%的数据, n <= 20。
对于 60%的数据, 保证输入中所有数小于 5000。
对于 100%的数据, 保证输入中所有数小于 5e5, K <= n。

====================================

对于 30%的数据, 可以直接枚举选数方案。
对于 50%的数据, 随便给的。
对于 100%的数据,开桶存下每个权值出现了几次,然后枚举 gcd。 枚举它的所有倍
数, 看出现次数是否大于等于 k 就可以了。 复杂度 O(n+n/2+n/3+…+n/n)=O(nlnn),证
明看“调和级数求和”。

我们来看看50%的数据该怎么打,其实考场上我应该是已经打出来的,只是为了变成10分,就10分了

20*30
伪代码://我已经没落到第一题还要打伪代码的结果了……

void dfs()
{
	int i,j;
	for(i=a[n-k+1];i>=1;--i)
	{
		int cnt=0;
		for(j=n;j>=1;--j)
		{
			if(a[i]%i==0)++cnt;
			if(a[i]<i)break;
		}
		if(cnt>=k)
		{
			printf("%d",i);
			break;
		}
	}
}

正解代码呢??
当然还是由我们wy扮演的std了

#include <cstdio>
#include <iostream>

const int N = 5e5 + 5;

inline int Get() {
	char ch;
	while ((ch = getchar()) < '0' || ch > '9');
	int Num = ch - '0';
	while ((ch = getchar()) >= '0' && ch <= '9')
		Num = (Num << 3) + (Num << 1) + ch - '0';
	return Num;
}

int n, K, w[N], mx;

int main() {
	freopen("gcd.in", "r", stdin);
	freopen("gcd.out", "w", stdout);
	n = Get(), K = Get();
	for (int i = 1, x; i <= n; ++i) 
		++w[x = Get()], mx = std :: max(mx, x);
	
	for (int i = mx; i > 0; --i) {
		int cnt = 0;
		for (int j = i; j <= mx; j += i) cnt += w[j];
	
		if (cnt >= K) {
			printf("%d\n", i);
			
			fclose(stdin);
			fclose(stdout);
			
			return 0;
		}
	}
}

计数(count)

【题目描述】

Alice 和 Bob 在平面直角坐标系中下棋。 Alice 的棋子初始时在(0, 0)位置, 要走到(a, b)位
置; Bob 的棋子初始时在(c, 0)位置, 要走到(a, d)位置。棋子只能沿 x 轴或 y 轴正方向移动若
干个单位长度, 问有多少种移动方案使两颗棋子的移动路径不相交。

【输入数据】

输入一行 4 个正整数, 依次为 a, b, c, d。

【输出数据】

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

【样例输入】

3 2 1 1

【样例输出】

6

【样例解释】

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

【数据范围】

对于 50%的数据, a + b <= 20。
对于 70%的数据, a + b <= 2e4。
对于 100%的数据, a + b <= 2e5 且 a > c, b > d。

================================================
这题我暴力嵌套dfs,50分

#include<bits/stdc++.h>
using namespace std;
const int N=1000+5;
const int dx[2]={0,1};
const int dy[2]={1,0};
const int MOD=100000007;
int A,B,C,D;
long long ans=0;
bool visa[N][N],visb[N][N];
void dfsb(int x,int y)
{
	int i;
    if(x==A&&y==D){++ans;ans%=MOD;return;}
    for(i=-1;++i<=1;)
	{
	    int _x=x+dx[i],_y=y+dy[i];
	    if(_x>A||_x<0||_y>D||_y<0)continue;
	    if(visa[_x][_y]||visb[_x][_y])continue;
	    visb[_x][_y]=1;
	    dfsb(_x,_y);
	    visb[_x][_y]=0;
	}
}
void dfsa(int x,int y)
{
	int i;
    if(x==A&&y==B){dfsb(C,0);return;}
    for(i=-1;++i<=1;)
	{
	    int _x=x+dx[i],_y=y+dy[i];
	    if(_x>A||_x<0||_y>B||_y<0)continue;
	    if(_x==C&&_y==0)continue;
	    if(visa[_x][_y])continue;
	    visa[_x][_y]=1;
	    dfsa(_x,_y);
	    visa[_x][_y]=0;
	}
}
int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%d%d%d%d",&A,&B,&C,&D);
	dfsa(0,0);
	printf("%lld",ans);
	return 0;
}

那满分??

首先,从(0, 0)到(n, m)一共有在这里插入图片描述种走法。
(一共要走 m+n 步,选其中 n 步来走 x 轴正方向的)
不考虑路径相交的情况, 总方案数为在这里插入图片描述种。
现在问题是, 这些方案中,有多少是路径相交的呢?
在这里插入图片描述
注意到如果两条路径相交,可以看成 A,B 棋子交换目的地。
相交的方案数为:
在这里插入图片描述
两个式子做差即可。
如何求组合数?
在这里插入图片描述
inverse(x)表示 x 的逆元,可利用费马小定理解出。
std 代码就是

#include <cstdio>

typedef long long ll;

const int mod = 1e8 + 7;
const int N = 2e5 + 5;

inline int Get() {
	char ch;
	while ((ch = getchar()) < '0' || ch > '9');
	int Num = ch - '0';
	while ((ch = getchar()) >= '0' && ch <= '9')
		Num = (Num << 3) + (Num << 1) + ch - '0';
	return Num;
}

int a, b, c, d, res1, res2, ans;
int fac[N], inv[N];

inline int pow(int x, int n) {
	if (!n) return 1;
	
	ll res = pow(x, n >> 1);
	res = res * res % mod;
	if (n & 1) res = res * x % mod;
	return res;
}

int main() {
	freopen("count.in", "r", stdin);
	freopen("count.out", "w", stdout);
	a = Get(), b = Get(), c = Get(), d = Get();
	
	fac[0] = 1;
	for (int i = 1; i < N; ++i) fac[i] = (ll)fac[i - 1] * i % mod;
	inv[N - 1] = pow(fac[N - 1], mod - 2);
	for (int i = N - 1; i; --i) inv[i - 1] = (ll)inv[i] * i % mod;
	
	res1 = ((ll)fac[a + b] * inv[a] % mod * inv[b] % mod) * ((ll)fac[a - c + d] * inv[a - c] % mod * inv[d] % mod) % mod;
	res2 = ((ll)fac[a + d] * inv[a] % mod * inv[d] % mod) * ((ll)fac[a - c + b] * inv[a - c] % mod * inv[b] % mod) % mod;
	ans = res1 - res2;
	if (ans < 0) ans += mod;

	printf("%d\n", ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

异色弧(arc)

【题目描述】

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

【输入数据】

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

【输出数据】

一行一个整数表示答案,答案对质数 1000000007 取模。

【样例输入】

8 1
2 3 1 2 3 2 1

【样例输出】

8

【数据范围】

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

========================================
暴力》》???
by yjh yjh博客点这里,虽然有点少

#include<bits/stdc++.h>
using namespace std;

const long long mo=1000000007;
long long ans;
int a[101000],b[101000],c[101000];
int n;

int main() {
	
	freopen("arc.in","r",stdin);
	freopen("arc.out","w",stdout); 
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[a[i]]++;
	
	b[a[1]]=1;
	for(int i=2;i<n;i++) {
		int k=0;
		b[a[i]]++;
		for(int j=i+1;j<=n;j++) {
			if (a[j] == a[i]) {
				k++;
				if (c[a[i]] - b[a[i]] - k == 0) break;
				continue;
			}
			ans+=(long long)b[a[j]]*(long long)(c[a[i]]-b[a[i]]-k);
			ans%=mo;
		}
	}
	
    printf("%lld",ans);
	return 0;
} 

正解还是看std
非常复杂的分类讨论
老师说看题解也要抄出来:
考虑容斥,计算所有异色圆弧对数减去AABB 式与ABBA 式
AABB式容易计算,枚举第二个A,左右两边的贡献可以线性得出
ABBA式的情况比较复杂,考虑平衡规划的思想
根据颜色的出现次数cnti分类,假设以k为界
分cntA >= k, cntA < k且cntB >= k, cntA < k且cntB < k三种情况进行讨论
记prei_j表示前i个点颜色j的出现次数

对应代码:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MaxN = 1e5 + 5;
const int T = 400;
const int Mod = 1e9 + 7;
const int Ie = 5e8 + 4;
ll total, prefix, suffix, res, r, sigma[MaxN], sum[MaxN], bit[MaxN];
int n, a[MaxN], m, li[MaxN], cnt[MaxN], K, pre[MaxN], suf[MaxN], A[MaxN], nA, nex[MaxN], last[MaxN], tot;
void BitInsert(int pos) {
	for (int k = pos; k <= n; k += k & -k) ++bit[k];
	return;
}
ll BitQuery(int pos) {
	ll res = 0;
	for (int k = pos; k > 0; k -= k & -k) res += bit[k];
	return res;
}
int main() {
	freopen("arc.in", "r", stdin);
	freopen("arc.out", "w", stdout);
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), li[++m] = a[i];
	sort(li + 1, li + m + 1), m = unique(li + 1, li + m + 1) - li - 1;
	for (int i = 1; i <= n; ++i) 
		++cnt[a[i] = lower_bound(li + 1, li + m + 1, a[i]) - li], nex[i] = last[a[i]], last[a[i]] = i;
	for (int i = 1; i <= m; ++i) {
		if (cnt[i] >= T) A[++nA] = i; suf[i] = cnt[i];
		if ((total += prefix * (r = ((ll)cnt[i] * (cnt[i] - 1) >> 1)) % Mod) >= Mod) total -= Mod;
		prefix += r;
	}
	
	//鍘婚櫎AABB鐨勬柟妗?
	suffix = prefix, res = 0;
	for (int i = 1, c; c = a[i], i <= n; ++i) {
		if ((res += (((suffix -= --suf[c]) - ((ll)suf[c] * (suf[c] - 1) >> 1)) * pre[c]) % Mod) >= Mod) res -= Mod;
		++pre[c];
	}
	if ((total -= res) < 0) total += Mod;
	
	//鍘婚櫎ABBA鐨勬柟妗?
	//task1:cntA >= T
	res = 0;
	for (int k = 1, c, cntA; prefix = 0, cntA = cnt[c = A[k]], k <= nA; ++k) {
		for (int w = 1; w <= m; ++w) sigma[w] = sum[w] = 0;
		for (int i = 1, w; w = a[i], i <= n; ++i) {
			if (w == c) ++prefix;
			else {
				if ((res += sum[w]) >= Mod) res -= Mod;
				if ((res -= sigma[w] * prefix % Mod) < 0) res += Mod;
				if ((sum[w] += prefix * cntA % Mod) >= Mod) sum[w] -= Mod;
				if ((sigma[w] += prefix) >= Mod) sigma[w] -= Mod;
			}
		}
	}
	if ((total -= res) < 0) total += Mod;
	
	//task2:cntA < T && cntB >= T 
	res = 0; 
	for (int k = 1, c; prefix = 0, c = A[k], k <= nA; ++k) {
		for (int w = 1; w <= m; ++w) sigma[w] = sum[w] = pre[w] = 0;
		for (int i = 1, w; w = a[i], i <= n; ++i)
			if (cnt[w] < T) {
				if ((res += (prefix * prefix - prefix) * (pre[w]++) % Mod) >= Mod) res -= Mod;
				if ((res += sum[w]) >= Mod) res -= Mod;
				if ((res -= 2ll * sigma[w] * prefix % Mod) < 0) res += Mod;
				if ((sum[w] += (prefix * prefix + prefix) % Mod) >= Mod) sum[w] -= Mod;
				if ((sigma[w] += prefix) >= Mod) sigma[w] -= Mod;
			}
			else prefix += (int)(w == c);
	}
	if ((total -= res * Ie % Mod) < 0) total += Mod;

	//task3:cntA < T && cntB < T
	res = 0;
	for (int i = 1; i <= n; ++i)
		if (cnt[a[i]] < T) {
			for (int k = nex[i]; k; k = nex[k]) 
				if ((res += tot - BitQuery(k)) >= Mod) res -= Mod;
			for (int k = nex[i]; k; k = nex[k]) ++tot, BitInsert(k);
		}
	if ((total -= res) < 0) total += Mod; //娉ㄦ剰杩欐牱浼氬鍑忓幓鍚屼竴绉嶉鑹茬殑涓や釜鐐瑰
	
	for (int w = 1; w <= m; ++w) //鍐嶅姞涓婂悓涓€绉嶉鑹茬殑涓や釜鐐瑰
		if (cnt[w] > 3 && cnt[w] < T) {
			ll r = (ll)cnt[w] * (cnt[w] - 1) * (cnt[w] - 2) * (cnt[w] - 3) / 24;
			if ((total += r % Mod) >= Mod) total -= Mod;
		}
	
	printf("%I64d\n", total % Mod);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

在这里插入图片描述
结束


在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值