【题解】Atcoder Beginner Contest 451(ABC451) A~F
A - illegal
判断。
#include<bits/stdc++.h>
using namespace std;
int l;
string s;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>s;
l=s.size();
if(l%5) cout<<"No";
else cout<<"Yes";
return 0;
}
B - Personnel Change
模拟。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int cnt1[110],cnt2[110];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
cnt1[x]++,cnt2[y]++;
}
for(int i=1;i<=m;i++){
cout<<cnt2[i]-cnt1[i]<<'\n';
}
return 0;
}
C - Understory
使用 multiset 或者 priority_queue 维护树高,以 \(O(\log n)\) 的时间插入(multiset 还需以此复杂度找到删除结束的位置),并以均摊 \(O(1)\) 的时间删除。
#include<bits/stdc++.h>
using namespace std;
int q;
multiset<int> tr;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>q;
for(int i=1;i<=q;i++){
int op,h;
cin>>op>>h;
if(op==1) tr.insert(h);
else tr.erase(tr.begin(),tr.upper_bound(h));
cout<<tr.size()<<'\n';
}
return 0;
}
D - Concat Power of 2
题意类似字符串拼接,用 \(2^k\) 拼出来长度小于等于 \(9\) 的数。
观察样例 3 发现在查询 \(10^6\) 量级时答案已经接近 \(10^9\),我们可以推测 \(\le10^9\) 的全部好整数就在 \(10^6\) 量级个。
我们可以用一个 BFS 枚举出所有的好整数,不断往后拼 \(2^k\) 得到新数加入队列。执行完发现一共有 \(1257874\) 个好整数,BFS 的搜索次数也在 \(3\times10^6\) 量级。
然后对着这大约 \(10^6\) 个好整数跑排序,输出第 \(n\) 个即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+10;
const int INF=1e9;
int n;
int p[40],tot;
int gt[N],idx;
map<int,bool> vis;
queue<int> q;
void bfs(){
q.push(0);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<=tot;i++){
int b=log(p[i])/log(10)+1;
int v=u*pow(10,b)+p[i];
if(v>INF) continue;
if(vis[v]) continue;
gt[++idx]=v;
vis[v]=1;
q.push(v);
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
p[0]=1;
while(p[tot]*2<=INF) p[tot+1]=p[tot]*2,tot++;
cout<<'\n';
bfs();
sort(gt+1,gt+1+idx);
cout<<gt[n];
return 0;
}
E - Tree Distance
根据树的性质,我们有:对于树上三点 \(i,j,k\),\(\operatorname{dis}(i,k)+\operatorname{dis}(k,j)\ge\operatorname{dis}(i,j)\)。仅当 \(k\) 在 \(i,j\) 的路径上,则等号成立。
那么若条件里存在 \(A_{i,k}+A_{k,j}<A_{i,j}\) 则一定无解。
但只知道这点依然是不可做的。一是暴力枚举 \(i,j,k\) 时间复杂度过高,二是不存在上述情况时也可能无解。
所以接下来我们就要想办法把最可能有解的树构造出来,再去判断这棵树是否满足所有条件。
为了方便起见我们均假设构造出来的树中 \(k\) 在 \(i,j\) 的路径上,\((i,k)\)、\((k,j)\) 为实际存在的路径且边权和分别等于 \(A_{i,k}\)、\(A_{k,j}\)。倘若这时 \(\operatorname{dis}(i,k)+\operatorname{dis}(k,j)>A_{i,j}\),那么构造出来的树不合法。
此时如果可以通过把 \((i,j)\) 变成实际路径代替 \((i,k)\) 或 \((k,j)\) 使上式不满足,则需要满足 \(A_{i,j}<A_{i,k}\) 或 \(A_{i,j}<A_{k,j}\)。
我们要构造最优的树,不会进行任何替换,也就是 \(A_{i,j}\) 一定是三者中最大的。为了使不被实际连起来的边最大,我们只需要跑最小生成树。接着跑 \(n\) 次 DFS 算出每两个点之间的距离,看看是否都等于 \(A_{i,j}\) 就可以了。
用 Kruskal 实现的时间复杂度 \(O(n^2\log n^2)\)。用 Prim 可以达到 \(O(n^2\log n)\)。均可通过此题。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3010;
int n,tot;
int g[N][N];
struct Node{
int from,to,w;
}e[N*N];
struct eNode{
int to,nxt,w;
}e2[N*N];
int h[N],toth;
void Add(int u,int v,int w){
toth++;
e2[toth].to=v;
e2[toth].w=w;
e2[toth].nxt=h[u];
h[u]=toth;
}
bool cmp(Node x,Node y){
return x.w<y.w;
}
int fa[N];
int Find(int x){
if(fa[x]==x) return x;
else return fa[x]=Find(fa[x]);
}
void Merge(int x,int y){
x=Find(x),y=Find(y);
fa[x]=y;
}
void Kruskal(){
for(int i=1;i<=n;i++) fa[i]=i;
int num=0;
for(int i=1;i<=tot;i++){
int u=e[i].from,v=e[i].to,w=e[i].w;
int fu=Find(u),fv=Find(v);
if(fu!=fv){
Merge(fu,fv);
Add(u,v,w),Add(v,u,w);
num++;
if(num==n-1) return ;
}
}
}
int dis[N][N];
void dfs(int s,int u,int fa){
for(int i=h[u];i;i=e2[i].nxt){
int v=e2[i].to;
if(v==fa) continue;
dis[s][v]=dis[s][u]+e2[i].w;
dfs(s,v,u);
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n-1;i++){
for(int j=i+1;j<=n;j++){
int w;
cin>>w;
e[++tot]=(Node){i,j,w};
g[i][j]=w;
}
}
sort(e+1,e+1+tot,cmp);
Kruskal();
for(int i=1;i<=n;i++) dfs(i,i,0);
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
if(dis[i][j]!=g[i][j]){
cout<<"No";
return 0;
}
}
}
cout<<"Yes";
return 0;
}
F - Make Bipartite 3
对于确定是否为二分图,用并查集维护连通性和颜色,若一条边两端在同一连通块且同色则从此以后不再是二分图。
对于维护颜色,用带权并查集维护节点到根边数的奇偶性和每个块 权为奇数的点 的数量。连接两个连通块时启发式合并确保时间复杂度正确。输出时输出权为奇 / 偶点数量中的最小值,可以每次合并前先分别减去两个块的该值,合并后再加上大块的该值。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,q,ans;
int fa[N],siz[N],val[N];
bool col[N];
int find(int x){
if(fa[x]==x) return x;
int f=fa[x];
fa[x]=find(fa[x]);
col[x]^=col[f];
return fa[x];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1,val[i]=1;
while(q--){
int u,v;
cin>>u>>v;
if(ans==-1){
cout<<"-1\n";
continue;
}
int fu=find(u),fv=find(v);
if(fu==fv){
if(col[u]==col[v]){
ans=-1;
cout<<"-1\n";
}else cout<<ans<<'\n';
continue;
}
if(siz[fu]>siz[fv]){
swap(fu,fv);
swap(u,v);
}
ans-=min(val[fu],siz[fu]-val[fu]);
ans-=min(val[fv],siz[fv]-val[fv]);
col[fu]=col[u]^col[v]^1;
if(col[fu]) val[fu]=siz[fu]-val[fu];
fa[fu]=fv;
val[fv]+=val[fu];
siz[fv]+=siz[fu];
ans+=min(val[fv],siz[fv]-val[fv]);
cout<<ans<<'\n';
}
return 0;
}

浙公网安备 33010602011771号