持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第18天,点击查看活动详情
【Codeforces】Codeforces Round #828 (Div. 3) 补题记录E2-F
E2 Divisible Numbers (hard version)
题目大意
给定 4 个正整数 ,其中 和 。找出满足下列条件的任意一对数 和 :
- ,
- 。
保证 。
思路
预处理出 和 的所有因数,记 的因数个数 , 的因数个数 。
枚举数对 ,其中 为 的因数, 为 的因数,则 可以取遍 的所有因数。
记 为 , 为 :
- 如果 的整数倍落在 到 中且 的整数倍落在 到 中,输出答案。
- 如果 的整数倍落在 到 中且 的整数倍落在 到 中,输出答案。
易知 以内的数最多有 1344 个因数,枚举数对 不会超时。
代码
#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
题目大意
给定一个 到 的排列 ,求 mex() 大于 med() 的 的对数。
mex(S) 表示数组 S 中没出现过的最小非负整数。
med(S) 表示数组 S 中所有元素从小到大排列后的第 个元素。
思路
先记录值为 的元素的位置 。
先思考 mex 值为 的满足上述条件的区间有多少个。
,描述了 mex 值为 的最小区间。则包含 ,长度不超过 且不包含 的区间即为一个合法区间。
如果我们随意枚举一个端点的位置,然后计算另一个端点有多少种可能,最坏情况下时间复杂度是 。为了减小时间复杂度,如果 我们就向左侧枚举,如果 我们就向右侧枚举,这样我们每个位置只会被枚举一次。
代码
#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;
}