[题解]AtCoder Beginner Contest 397(ABC397) A~F
A - Thermometer
按题意判断并输出即可。
时间复杂度\(O(1)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
double x;
signed main(){
cin>>x;
if(x>=38) cout<<"1\n";
else if(x>=37.5) cout<<"2\n";
else cout<<"3\n";
return 0;
}
B - Ticket Gate Log
令\(c\)表示当前位应为哪个字符,初始为i。
从左到右遍历每个字符\(s_i\):
- 如果\(s_i=c\),就将\(c\)取反;
- 如果\(s_i\ne c\),就累加\(1\)的贡献。
时间复杂度\(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
string s;
int ans=0;
char c='i';
signed main(){
cin>>s;
for(char i:s){
if(i==c) c=(c=='i'?'o':'i');
else ans++;
}
cout<<ans+(c=='o')<<"\n";
return 0;
}
C - Variety Split Easy
分别令\(lef_i,rig_i\)表示\(A\)长度为\(i\)的前缀和后缀有多少不同的元素。
则答案为\(\max\limits_{i=1}^{n-1} (lef_i+rig_i)\)。
\(lef,rig\)可以在遍历过程中,用桶计算出来。
可以用数组、unordered_set等数据结构作为桶。
时间复杂度为\(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
#define N 300010
using namespace std;
int n,a[N],ans;
unordered_set<int> se;
int lef[N],rig[N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
se.insert(a[i]);
lef[i]=se.size();
}
se.clear();
for(int i=n;i>=1;i--) se.insert(a[i]),rig[i]=se.size();
for(int i=1;i<n;i++) ans=max(ans,lef[i]+rig[i+1]);
cout<<ans<<"\n";
return 0;
}
D - Cubes
因式分解可得:\(x^3-y^3=(x-y)(x^2+xy+y^2)\)。
如果我们可以枚举\(d=x-y\),即\(x=d+y\)……
那我们仅需判断\(N=d[(d+y)^2+(d+y)y+y^2]\),即\(3y^2+3dy+(d^2-\frac{N}{d})=0\)是否有正整数解即可。
由于\(x^2+xy+y^2\ge (x-y)^3\),所以\((x-y)^3\le N\),即\(d\le \sqrt[3]{N}\),因此枚举\(d\)的时间复杂度是\(O(\sqrt[3]{N})\)。
接下来考虑对于每个\(d\),如何判断是否有整数解。
- 可以用二次方程求根公式完成判定,时间复杂度为\(O(1)\),不过实现起来比较繁琐;
- 考虑到二次项、一次项系数都为正,所以上式在\(x>0\)时是单增的,所以我们也可以使用二分法。
显然仅需在\([1,\sqrt N]\)内二分即可,时间复杂度为\(O(\log\sqrt N)\),实现起来较为简单。
代码使用二分法,总时间复杂度为\(O(\sqrt[3]{N}\log\sqrt N)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int solve(int a,int b,int c){
int l=1,r=1e9;
while(l<r){
int x=(l+r+1)>>1;
if(a*x*x+b*x+c<=0) l=x;
else r=x-1;
}
return a*l*l+b*l+c==0?l:-1;
}
signed main(){
cin>>n;
for(int d=1;d*d*d<=n;d++){
if(n%d) continue;
int y=solve(3,3*d,d*d-n/d);
if(~y) cout<<d+y<<" "<<y<<"\n",exit(0);
}
cout<<"-1\n";
return 0;
}
E - Path Decomposition of a Tree
定义\(siz_u\)为子树\(u\)的大小,\(fa_u\)为\(u\)的父节点,\(ch_u\)为\(u\)的子节点个数。
定义\(t_u=siz_u\bmod k\)。不难发现,其含义就是“为了将子树\(u\)的节点全部用链覆盖,子树\(u\)外最少要用多少个节点”。
比如\(t_u=0\)就表示子树\(u\)的答案为Yes。
选定一个节点\(R\)作为根节点,开始遍历每棵树,对于节点\(u\),输出No的情况显然只有下面\(3\)种:
- \((\sum\limits_{fa_v=u} [t_v\ne 0])>2\)。
- \((\sum\limits_{fa_v=u} t_v)+1>k\)。
- \(ch_u=2\)且\((\sum\limits_{fa_v=u} t_v)\ne k\)。
特别地,对于根节点\(R\),如果\(t_R\ne 0\),输出No。
不过根据数据范围,节点数一定是\(k\)的倍数,所以一定有\(t_R=0\),就不用讨论了。
时间复杂度\(O(nk)\)。
点击查看代码
#include<bits/stdc++.h>
#define NN 200010
#define int long long
using namespace std;
int n,k;
vector<int> G[NN];
void add(int u,int v){G[u].emplace_back(v);}
int dfs(int u,int fa){
int cnt=0,sum=1;
for(int i:G[u]){
if(i==fa) continue;
int t=dfs(i,u);
if(!t) continue;
if(cnt==2) cout<<"No\n",exit(0);
sum+=t,cnt++;
}
if(sum>k||(cnt==2&&sum!=k)) cout<<"No\n",exit(0);
return sum%k;
}
signed main(){
cin>>n>>k;
for(int i=1,u,v;i<n*k;i++){
cin>>u>>v;
add(u,v),add(v,u);
}
dfs(1,0);
cout<<"Yes\n";
return 0;
}
F - Variety Split Hard
如果第\(2\)个区间的右端点已经确定为\(i\),我们仅需考虑第\(1\)个区间的右端点如何选择,即\(a_{1\sim i}\)分成\(2\)个区间的答案。
先考虑用另一种方法解决C——统计每一个位置对答案的贡献。
- 如果\(a_i\)在\(a_{1\sim i}\)中是唯一的,那么第\(1\)个区间的右端点设在任何位置都会产生\(1\)的贡献。
- 如果\(a_i\)在\(a_{1\sim i}\)中不是唯一的,定义\([1,i)\)中最大的\(p\)使得\(a_p=a_i\),那么:
- 如果第\(1\)个区间的右端点\(\in [p,i)\),则对该位置产生\(1\)的贡献。
- 否则对该位置产生\(0\)的贡献。
用线段树维护每个位置的总贡献,这些总贡献的最大值即为答案。
之所以用这种方法来考虑C题,是因为它可以很轻易地被推广。仅需顺序遍历\(i\in [1,n]\),在统计完前\(i\)个的贡献之后,就用\([1,i]\)每个位置总贡献的最大值,再加上“\(a_{(i+1)\sim n}\)中互不相同的值的个数”,更新答案即可。
按上面的步骤,会把区间为空的情况一并统计进来。不过想一想就能发现,存在空区间的答案一定不会优于每个区间都非空的答案。所以正确性是可以保证的。
时间复杂度\(O(n\log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 300010
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
int n,a[N],suf[N],last[N],ans;
bitset<N> vis;
struct Node{int tag,maxx;}s[N<<2];
void build(int x,int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
build(lc,l,mid),build(rc,mid+1,r);
}
void pushdown(int x){
s[lc].maxx+=s[x].tag,s[lc].tag+=s[x].tag;
s[rc].maxx+=s[x].tag,s[rc].tag+=s[x].tag;
s[x].tag=0;
}
void chr(int x,int a,int b,int l,int r,int v){
if(a<=l&&r<=b) return s[x].maxx+=v,s[x].tag+=v,void();
pushdown(x);
int mid=(l+r)>>1;
if(a<=mid) chr(lc,a,b,l,mid,v);
if(b>mid) chr(rc,a,b,mid+1,r,v);
s[x].maxx=max(s[lc].maxx,s[rc].maxx);
}
int query(int x,int a,int b,int l,int r){
if(a<=l&&r<=b) return s[x].maxx;
pushdown(x);
int mid=(l+r)>>1,ans=0;
if(a<=mid) ans=max(ans,query(lc,a,b,l,mid));
if(b>mid) ans=max(ans,query(rc,a,b,mid+1,r));
return ans;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=n;i>=1;i--) suf[i]=suf[i+1]+!vis[a[i]],vis[a[i]]=1;
build(1,1,n);
for(int i=1;i<=n;i++){
if(last[a[i]]) chr(1,last[a[i]],i-1,1,n,1);
else chr(1,1,n,1,n,1);
last[a[i]]=i;
ans=max(ans,suf[i+1]+query(1,1,i,1,n));
}
cout<<ans<<"\n";
return 0;
}
这道题还有加强版:https://codeforces.com/problemset/problem/833/B,如果有空会写题解。
浙公网安备 33010602011771号