Codeforces Round #613(div2)解题报告
作者:互联网
Codeforces Round #613(div2)解题报告
A. Mezo Playing Zoma
- 好像直接输出\(n+1\)就行了?那我应该写复杂了。
- 懒得改了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T;
int main()
{
int n;
cin >> n;
string str;
cin >> str;
int n1 = 0, n2 = 0;
for(int i = 0; i < str.size(); i++)
{
if(str[i] == 'L') n1++;
else n2++;
}
int ans = n1 + n2 + 1;
cout << ans << endl;
return 0;
}
B. Just Eat It!
- 首先求出所有数字的和。
- 对于另一个人选择子段,那么其实就是求最大子段和,属于入门dp问题。
- \(f(i)=max(a(i),f(i-1)+a(i))\)。
- 那么求出最大子段和,同时遍历出这个子段和最短的可能的区间,然后判定。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int T, n;
ll a[maxn], sum, f[maxn], s[maxn];
void solve()
{
sum = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
f[i] = s[i] = 0;
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
sum += a[i];
s[i] = s[i-1] + a[i];
}
ll maxx = -0x3f3f3f3f;
for(int i = 1; i <= n; i++)
{
f[i] = max(a[i], f[i-1] + a[i]);
maxx = max(maxx, f[i]);
}
int r = 0, l = 0;
//正序遍历保证区间最短
for(int i = 1; i <= n; i++)
if(f[i] == maxx)
{
r = i;
break;
}
for(int i = r; i >= 1; i--)
if(s[r] - s[i-1] == maxx)
{
l = i;
break;
}
if(sum > maxx) puts("YES");
else {
if(sum == maxx)
{
if(l == 1 && r == n) puts("YES");
else puts("NO");
}
else puts("NO");
}
}
int main()
{
scanf("%d", &T);
while(T--) solve();
return 0;
}
C. Fadi and LCM
- 给定一个数字\(x\leq10^{12}\),求两个数字\(a,b\)使得\(max(a,b)\)尽可能的小且\(lcm(a,b)=x\)。
- 好像正解是分解质因数后枚举方案?
- 仔细想了一下的话就会发现:
- 首先,两个数字要互质。
- 第二:两个数字尽可能的接近。
- 为了让两个数字尽量接近,那么就可以直接开个根号,然后往前枚举,开根号后数据是\(10^6\),\(gcd\)的复杂度在\(log\),可以通过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
ll gcd(ll a, ll b)
{
if(b == 0) return a;
return gcd(b, a%b);
}
int main()
{
ll x; cin >> x;
if(x == 1) {
cout << "1 1" << endl;
return 0;
}
ll a = sqrt(x);
while(1)
{
ll b = x / a;
if(a * b == x && gcd(a, b) == 1){
cout << a << " " << b << endl;
return 0;
}
a--;
}
return 0;
}
D. Dr. Evil Underscores
- 给定一个序列\(a_i\),找一个数字使得\(max_{1\leq i\leq n}a_i\land X\)最小,求出这个最小值。
- 贪心+分治。
- 从最高位开始贪心,如果此时数组元素的这一位全为0或者全为1,那么可以把它们全部贪掉。
- 如果有的是1,有的是0,那说明这一位是躲不掉的,这一位肯定为1,所以在返回的时候加上一个\(1<<i\)。
- 而对于后面的数字分治取最小值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> a;
int n;
int solve(vector<int> v, int bit)
{
if(bit < 0 || v.size() == 0) return 0;
vector<int> z, o;
for(int i = 0; i < v.size(); i++)
{
if((v[i]>>bit) & 1) z.push_back(v[i]);
else o.push_back(v[i]);
}
if(o.size() == 0) return solve(z, bit-1);
if(z.size() == 0) return solve(o, bit-1);
return (min(solve(o, bit-1), solve(z, bit-1))|1<<bit);
}
int main()
{
scanf("%d", &n);
for(int i = 1, x; i++ <= n;)
scanf("%d", &x), a.push_back(x);
cout << solve(a, 30) << endl;
}
E. Delete a Segment
- 给你n个线段,union of the set of segments指的是几个线段两两之间互不相交的线段个数。
- 问准确的拿走一个线段,问拿走哪个线段,剩余的线段组成的union of the set of segments数量最大。
- 洛谷题解。
- 暴力做法是枚举删除的线段后\(O(n)\)扫一遍,利用并查集判断最后有几个线段。
- 但其实可以先求出所有的线段答案,之后枚举删除的线段。
- 对于一个线段,删除后有可能造成产生更多的结果。
- 所以枚举之后记录一下最大的增长量,加上最初的结果,就是最终的结果。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
int n;
struct segment{
ll l, r;
}s[maxn];
ll ans0, ans1;
ll tmp[maxn<<1], d_[maxn<<2], d[maxn<<2];
void solve()
{
ans0 = 0;
ans1 = -0x3f3f3f3f;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &s[i].l, &s[i].r);
tmp[(i<<1)-1] = s[i].l;
tmp[i<<1] = s[i].r;
}
sort(tmp+1, tmp+(n<<1)+1);
int tot = unique(tmp+1, tmp+(n<<1)+1) - tmp - 1;
for(int i = 1; i <= n; i++)
{
s[i].l = lower_bound(tmp+1, tmp+1+tot, s[i].l) - tmp;
s[i].r = lower_bound(tmp+1, tmp+1+tot, s[i].r) - tmp;
s[i].l <<= 1ll, s[i].r <<= 1ll;
d[s[i].l]++, d[s[i].r+1]--; //差分序列
}
tot <<= 1;
for(int i = 1; i <= tot+5; i++)
d[i] += d[i-1];
//初始答案
for(int i = 0; i <= tot+5; i++)
ans0 += d[i] && !d[i+1];
for(int i = 0; i <= tot+4; i++)
if(d[i] == 1 && d[i+1] != 1)
d_[i]++;
for(int i = 1; i <= tot+5; i++)
if(d[i] == 1 && d[i-1] != 1)
d_[i]++;
for(int i = 1; i <= tot+5; i++)
d_[i] += d_[i-1];
for(int i = 1; i <= n; i++)
{
ll t = (d_[s[i].r] - d_[s[i].l-1]) / 2;
t -= (d[s[i].r] == 1) + (d[s[i].l] == 1);
ans1 = max(ans1, t);
}
cout << ans0 + ans1 << endl;
for(int i = 0; i <= tot+10; i++)
d[i] = d_[i] = 0;
}
int main()
{
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}
F. Classical?
题意:
- 给定\(n\)个数,求\(max_{1\leq i\leq j\leq n}lcm(a_i,a_j)\)。
- 数据范围:\(n,a_i\leq 1e5\)。
思路:
我们知道\(lcm(a,b)=\frac{ab}{gcd(a,b)}\)。
考虑枚举\(gcd(a,b)=1,2,3,...=g\)。
那么我们现在考虑考虑\(g\)的倍数,将这些倍数全部除以\(g\),那么有\(gcd(x,y)=g=>gcd(x/g,y/g)=1\)。
所以其实我们只用考虑互质的那种情况,不互质可以转换成互质。
从大到小扫描集合里的数。
假如说扫描到有个数字\(x\),存在\(y\)有\(y>x\)且\(gcd(x,y)=1\),那么所有满足\(x<z<y\)的数\(z\)不会再对答案有贡献了。因为\(gcd(z,x)\)可能不等于\(1\),而且\(z*x\)一定小于\(x*y\)。
因此用栈存储扫过的元素,扫到一个数\(x\)时,只要栈中有与\(x\)互质的数就pop,并与\(x\)更新答案。
如果判断栈中是否有与x互质的元素?
与\(x\)互质的数的个数为:
- \(\phi(x)\)。
- 有\(\sum_{d|n}\frac{\mu(d)}{d}=\frac{\phi(n)}{n}\).
- 有\(\phi(n)=\sum_{d|n}\mu(d)*\frac{n}{d}\)。
- 假设\(cnt_d\)表示栈中\(d\)的倍数数量。
那么栈中与\(x\)互质的数的数量有:
- \(\sum_{d|x}\mu(d)cnt_d\)。
其中\(\mu(d)\)是莫比乌斯函数。
所以只需要遍历完这些与\(x\)互质的数,然后取最大的就行了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int n, cnt[maxn];
bool b[maxn], vis[maxn];
vector<int> d[maxn];
ll ans;
ll gcd(ll a, ll b)
{
if(b == 0) return a;
return gcd(b, a%b);
}
int mu[maxn], primes[maxn], tot;
void get_mu(int n)
{
mu[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!vis[i])
{
primes[++tot] = i;
mu[i] = -1;
}
for(int j = 1; primes[j] <= n/i; j++)
{
vis[primes[j]*i] = 1;
if(i % primes[j] == 0) break;
else mu[i*primes[j]] = -mu[i];
}
}
//预处理出所有倍数
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j += i)
d[j].push_back(i);
}
ll coprime(ll x)
{
ll res = 0;
for(int i = 0; i < d[x].size(); i++)
res += cnt[d[x][i]] * mu[d[x][i]];
return res;
}
void update(int x, int a)
{
for(int i = 0; i < d[x].size(); i++)
cnt[d[x][i]] += a;
}
int main()
{
get_mu(maxn-10);
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
ll x; scanf("%lld", &x);
ans = max(ans, x);
b[x] = 1;
}
stack<int> s;
//枚举gcd=g
for(int g = 1; g <= maxn-10; g++)
{
for(int i = (maxn-10)/g; i > 0; i--)
{
//没有这个数字 跳过
if(!b[i*g]) continue;
//c表示栈中与i互质的数的数量
int c = coprime(i);
while(c)
{
if(gcd(s.top(), i) == 1)
{
ans = max(ans, 1ll*i*s.top()*g);
c--;
}
//出栈
update(s.top(), -1);
s.pop();
}
update(i, 1);
s.push(i);
}
//空栈
while(s.size())
{
update(s.top(), -1);
s.pop();
}
}
cout << ans << endl;
return 0;
}
标签:gcd,int,613,ll,Codeforces,long,maxn,互质,Round 来源: https://www.cnblogs.com/zxytxdy/p/12210186.html