【Codeforces】Codeforces Round #828 (Div. 3) 补题记录E2-F

245 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第18天,点击查看活动详情

【Codeforces】Codeforces Round #828 (Div. 3) 补题记录E2-F

E2 Divisible Numbers (hard version)

题目大意

给定 4 个正整数 a,b,c,da,b,c,d,其中 a<ca<cb<db<d。找出满足下列条件的任意一对数 xxyy

  • a<xc,b<yda<x\le c, b<y\le d,
  • a×bx×ya\times b\mid x\times y

保证 1a,b,c,d1091\le a,b,c,d\le 10^9

思路

预处理出 aabb 的所有因数,记 aa 的因数个数 m1m_1bb 的因数个数 m2m_2

枚举数对 u,vu,v,其中 uuaa 的因数,vvbb 的因数,则 u×vu\times v 可以取遍 a×ba\times b的所有因数。
u×vu\times vxxa×bu×v\frac{a\times b}{u\times v}yy

  • 如果 xx 的整数倍落在 a+1a+1cc 中且 yy 的整数倍落在 b+1b+1dd 中,输出答案。
  • 如果 yy 的整数倍落在 a+1a+1cc 中且 xx 的整数倍落在 b+1b+1dd 中,输出答案。

易知 10910^9 以内的数最多有 1344 个因数,枚举数对 u,vu,v 不会超时。

代码

#include <bits/stdc++.h>
using namespace std;
using LL=long long;
const int N=500001;
vector<int> e[N];
int n,m,k;
vector<LL> p1,p2;
LL a,b,c,d,x,y,z;
int check(LL x,LL y)
{
	x=(a/x+1)*x;
	y=(b/y+1)*y;
	if (x<=c&&y<=d) return printf("%lld %lld\n",x,y),1;
	return 0;
}
int solve()
{
	p1.clear();
	p2.clear();
	scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
	for (int i=1;i*i<=a;++i)
		if (a%i==0)
			if (p1.push_back(i),i*i!=a) p1.push_back(a/i);
	for (int i=1;i*i<=b;++i)
		if (b%i==0)
			if (p2.push_back(i),i*i!=b) p2.push_back(b/i);
	for (auto u:p1)
		for (auto v:p2)
		{
			x=u*v;
			y=a*b/u/v;
			if (check(x,y)) return 0;
			if (check(y,x)) return 0;
		}
	printf("-1 -1\n");
	return 0;
}
int main()
{
	int T=1;
	for (scanf("%d",&T);T--;) solve();
	return 0;
}

F MEX vs MED

题目大意

给定一个 00n1n-1 的排列 a1,a2,...,ana_1,a_2,...,a_n,求 mex(al,al+1,...,ara_l,a_{l+1},...,a_r) 大于 med(al,al+1,...,ara_l,a_{l+1},...,a_r) 的 l,rl,r 的对数。

mex(S) 表示数组 S 中没出现过的最小非负整数。

med(S) 表示数组 S 中所有元素从小到大排列后的第 S+12\left\lfloor \frac{|S|+1}{2} \right\rfloor个元素。

思路

先记录值为 xx 的元素的位置 id[x]id[x]

先思考 mex 值为 ii 的满足上述条件的区间有多少个。

L=min{id[0],id[1],...,id[i1]},R={id[0],id[1],...,id[i1]}L=min\{id[0],id[1],...,id[i-1]\},R=\{id[0],id[1],...,id[i-1]\},描述了 mex 值为 ii 的最小区间。则包含 [L,R][L,R],长度不超过 2×i2\times i 且不包含 ii 的区间即为一个合法区间。

如果我们随意枚举一个端点的位置,然后计算另一个端点有多少种可能,最坏情况下时间复杂度是 O(n2)O(n^2)。为了减小时间复杂度,如果 id[i]<Lid[i]<L 我们就向左侧枚举,如果 id[i]>Rid[i]>R 我们就向右侧枚举,这样我们每个位置只会被枚举一次。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=500001;
int n,m,k;
int id[N];
int solve()
{
	scanf("%d",&n);
	for (int i=1,x;i<=n;++i)
	{
		scanf("%d",&x);
		id[x]=i;
	}
	id[n]=1<<30;
	int l=id[0],r=id[0];
	long long ans=0;
	for (int i=1;i<=n;++i)
	{
		l=min(l,id[i-1]);
		r=max(r,id[i-1]);
		if (id[i]<l)
		{
			for (int x=l,y;x>id[i];--x)
			{
				y=x+2*i-1;
				y=min(y,n);
				if (y<r) break;
				ans+=y-r+1;
			}
		}
		if (id[i]>r)
		{
			for (int y=r,x;y<id[i]&&y<=n;++y)
			{
				x=y-2*i+1;
				x=max(x,1);
				if (x>l) break;
				ans+=l-x+1;
			}
		}
	}
	printf("%lld\n",ans);
}
int main()
{
	int T=1;
	for (scanf("%d",&T);T--;) solve();
	return 0;
}