AtCoder Beginner Contest 469 题解
D - The Big Two
没有怎么思考,写法可能比较暴力。
- 容易发现对于第一场比赛,设晋级的为 \(u\),\(v\),那么合法的组一定是 \((u,x) / (v,x)\)。
- 我们分开处理,以 \(u\) 为例,遍历找到第一场没有出现 \(u\) 的比赛,设该场比赛出现的是 \(s,t\)
- 那么答案只能是 \((u,s)/(u,t)\) ,再次遍历分别判断即可。
- 注意如果 \((u,v)\) 是合法情况会重复计算,记得减去。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
#define x first
#define y second
const int N=8e5+10,inf=0x3f3f3f3f,mod=998244353;
int n,m,q;
pii a[N];
void solve() {
cin>>n>>m;
int res=0;
for(int i=1;i<=m;i++) {
cin>>a[i].x>>a[i].y;
}
int u=a[1].x,v=a[1].y;
int s=0,t=0;
for(int i=1;i<=m;i++) {
if(a[i].x!=u && a[i].y!=u) {
s=a[i].x,t=a[i].y;
}
}
if(!s) res+=n-1;
else {
// u s
bool sg=0;
for(int i=1;i<=m;i++) {
if(a[i].x!=u && a[i].y!=u && a[i].x!=s && a[i].y!=s) {
sg=1;
break;
}
}
if(!sg) res++;
//u t
sg=0;
for(int i=1;i<=m;i++) {
if(a[i].x!=u && a[i].y!=u && a[i].x!=t && a[i].y!=t) {
sg=1;
break;
}
}
if(!sg) res++;
}
// cout<<u<<" "<<s<<":"<<t<<"\n";
s=0,t=0;
for(int i=1;i<=m;i++) {
if(a[i].x!=v && a[i].y!=v) {
s=a[i].x,t=a[i].y;
}
}
if(!s) res+=n-1;
else {
// v s
bool sg=0;
// cout<<v<<"--"<<s<<"\n";
for(int i=1;i<=m;i++) {
if(a[i].x!=v && a[i].y!=v && a[i].x!=s && a[i].y!=s) {
sg=1;
break;
}
}
if(!sg) res++;
//v t
sg=0;
for(int i=1;i<=m;i++) {
if(a[i].x!=v && a[i].y!=v && a[i].x!=t && a[i].y!=t) {
sg=1;
break;
}
}
if(!sg) res++;
}
bool sg=0;
for(int i=1;i<=m;i++) {
if(a[i].x!=v && a[i].y!=v && a[i].x!=u && a[i].y!=u) {
sg=1;
break;
}
}
// cout<<res<<"\n";
if(!sg) res--;
cout<<res<<"\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;T=1;
while(T--) solve();
return 0;
}
/*
1
3 3 2
1 2 2
2 3 3
3 1 4
2 1
1 1
*/
E - Pro Exam Eligibility
- 我们只关心一个区间内 'o' 的数量,将所有为 \(o\) 的点提出,设第 \(i\) 个的位置为 \(P_i\)。
- 显然答案具有单调性,考虑二分答案。
- 对于一个 \(mid\) 合法的条件是存在一个区间 \([i,j]\) ,满足 \((j-i+1)/(P_i-P_j)>=mid\)
- 分离参数得 \((P_j*mid-j)-(P_i*mid-i)<=1-mid\)
- 枚举 \(j\) ,记录最小的 \(P_i*mid-i\) 即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
#define x first
#define y second
const int N=8e5+10,inf=0x3f3f3f3f,mod=998244353;
const double eps=1e-10;
int n,k,q;
pii a[N];
string s;
vector<int> vec;
bool chk(double x) {
double maxx=0.0;
int m=vec.size();
for(int i=k-1;i<m;i++) {
int nj=i-k+1;
maxx=max(maxx,vec[nj]*x-nj);
double now=vec[i]*x-i;
if(now-maxx<=1.0-x) return 1;
}
return 0;
}
void solve() {
cin>>n>>k;
cin>>s;
s=" "+s;
for(int i=1;i<=n;i++) {
if(s[i]=='o') vec.push_back(i);
}
double l=0.0,r=1.0;
while(r-l>eps) {
double mid=(l+r)/2;
if(chk(mid)) l=mid;
else r=mid;
}
cout<<fixed<<setprecision(10)<<l<<"\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;T=1;
while(T--) solve();
return 0;
}
/*
1
3 3 2
1 2 2
2 3 3
3 1 4
2 1
1 1
*/
F - GCD Maximum Spanning Tree
感觉有点典
- 易知最大权值不超过 \(max{(a_i)}\),设为 \(mx\)。
- 考虑 Kruskal 的连边思想,先把权值都为 \(val\) 的点连边,因为与它们相连的点最大权值不会超过 \(val\)。
- 然后 \(mx\) 倒叙枚举,枚举 \(val=ki\) 的点,如果没有连在一起就相连,因为倒叙枚举,所以它们之间的 \(\gcd\) 一定是 \(i\) ,贡献为 \(i\)。
- 连边合并用并查集维护即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
#define x first
#define y second
const int N=1e6+10,inf=0x3f3f3f3f,mod=998244353;
const double eps=1e-10;
int n,k,q;
int a[N],p[N];
string s;
vector<int> vec[N];
inline int find(int x) {
return p[x]==x?p[x]:p[x]=find(p[x]);
}
bool merge(int a,int b) {
a=find(a),b=find(b);
if(a==b) return 0;
p[b]=a;
return 1;
}
void solve() {
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) vec[a[i]].push_back(i);
int mx=0;
for(int i=1;i<=n;i++) mx=max(a[i],mx);
for(int i=1;i<=mx;i++) p[i]=i;
ll res=0;
for(int i=1;i<=mx;i++) {
if(vec[i].size()) {
res+=1ll*(vec[i].size()-1)*i;
}
}
for(int i=mx;i>=1;i--) {
int fi=0;
for(int j=i;j<=mx;j+=i) {
if(vec[j].size() && !fi) fi=j;
else {
if(vec[j].size()) {
// merge(j,fi);
if(merge(j,fi)) res+=i;
// cout<<i<<"-"<<res<<"\n";
}
}
}
}
cout<<res<<"\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;T=1;
while(T--) solve();
return 0;
}
/*
1
3 3 2
1 2 2
2 3 3
3 1 4
2 1
1 1
*/

浙公网安备 33010602011771号