AtCoder Beginner Contest 350 解题报告
AtCoder Beginner Contest 350
A - Past ABCs
当且仅当串为 \(\texttt{ABC000},\texttt{ABC316},\texttt{ABC350}\sim\texttt{ABC999}\) 时输出 \(\texttt{No}\)。
(本人因 \(000\) 挂了一发。)
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
int x;
scanf("ABC%d",&x);
puts(x<=349&&x!=316&&x?"Yes":"No");
return 0;
}
B - Dentist Aoki
模拟。
#include<bits/stdc++.h>
using namespace std;
int n,k,a[1005];
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n>>k;
fill(a+1,a+n+1,1);
while(k--)
{
int x;cin>>x;
a[x]^=1;
}
cout<<accumulate(a+1,a+n+1,0);
return 0;
}
C - Sort
对于每个 \(i=1,2,\ldots,n-1\),依次将 \(i\) 归位即可。\(\Theta(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,a[N],p[N];
vector<PII>ans;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],p[a[i]]=i;
for(int i=1;i<n;i++)
if(a[i]!=i)
{
ans.PB(i,p[i]);
swap(a[i],a[p[i]]),swap(p[i],p[a[p[i]]]);
}
cout<<ans.size()<<endl;
for(auto i:ans)cout<<i.fi<<" "<<i.se<<endl;
return 0;
}
D - New Friends
可以发现每个连通块最后会被连成完全图。并查集维护即可。\(\Theta(n\alpha(n))\)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,m;
int fa[N],cnt[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n>>m;
iota(fa+1,fa+n+1,1);
for(int i=1,x,y;i<=m;i++)
cin>>x>>y,fa[find(x)]=find(y);
for(int i=1;i<=n;i++)cnt[find(i)]++;
ll ans=-m;
for(int i=1;i<=n;i++)ans+=cnt[i]*(cnt[i]-1ll)/2;
cout<<ans;
return 0;
}
E - Toward 0
由于 \(\left\lfloor\dfrac{\lfloor\frac nx\rfloor}y\right\rfloor=\left\lfloor\dfrac{\lfloor\frac ny\rfloor}x\right\rfloor=\left\lfloor\dfrac n{xy}\right\rfloor\),而骰子只有 \(1,2,3,4,5,6\) 的点数,可以发现可能到达的值一定形如 \(\left\lfloor\dfrac n{2^i3^j5^k}\right\rfloor\),这样的值是 \(\Theta(\mathrm{polylog}(n))\) 的。
然后就可以 dp 了,注意掷到 \(1\) 的情况:
时间:\(\Theta(\mathrm{polylog}(n))\)。
#include<bits/stdc++.h>
using namespace std;
ll n;
int a,x,y;
map<ll,double>f;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n>>a>>x>>y;
f[0]=0;
for(ll i=1;i<=n;i<<=1)
for(ll j=1;i*j<=n;j*=3)
for(ll k=1;i*j*k<=n;k*=5)
f[n/(i*j*k)]=0;
for(auto &i:f)
{
if(!i.fi)i.se=0;
else
{
double s1=x+f[i.fi/a],s2=6.0*y;
for(int j=2;j<=6;j++)
s2+=f[i.fi/j];
s2/=5;
i.se=min(s1,s2);
}
}
cout<<fixed<<setprecision(20)<<f[n];
return 0;
}
F - Transpose
这能给 \(550\) 分?
先把与每个括号匹配的括号的位置求出来,然后类似括号树地递归就行了。遇到括号在内部倒着扫,大小写随便处理一下就行了。\(\Theta(|S|)\)。
#include<bits/stdc++.h>
using namespace std;
string s;
int pr[500005];
void sol(int x,int y)
{
dbg(x,y);
if(x<=y)
{
for(int i=x;i<=y;i++)
{
if(s[i]=='('){if(pr[i]!=i+1)sol(pr[i]-1,i+1);i=pr[i];}
else cout<<s[i];
}
}
else
{
for(int i=x;i>=y; i--)
{
if(s[i]==')'){if(pr[i]!=i-1)sol(pr[i]+1,i-1);i=pr[i];}
else cout<<s[i];
}
}
}
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>s;
VI stk;
for(int i=0;i<s.size();i++)
if(s[i]=='(')stk.PB(i);
else if(s[i]==')')pr[stk.back()]=i,pr[i]=stk.back(),stk.PPB();
else
{
if(stk.size()&1)
{
if(s[i]>='a')s[i]-=32;
else s[i]+=32;
}
}
sol(0,s.size()-1);
return 0;
}
G - Mediator
Shaber 题。
一种 naive 的想法是,用 bitset
维护相邻点集,查询时直接与一下就行了。但是空间会爆炸。
考虑套一层根号分治,如果一个点的度数不超过 \(B\) 就暴力。
具体地,如果加边时发现一个点的度数超过了 \(B\),就新开一个 bitset
存下。
查询时,如果有一个点的度数不超过 \(B\),那么就枚举这个点的相邻点暴力判断;否则就把它们的相邻点集与一下再看是否有 set bit 即可。
取 \(B=\Theta(\sqrt n)\),时空复杂度 \(\Theta\left(n\sqrt n+\dfrac{n^2}w\right)\)。(假设 \(n,q\) 同阶)
另:这个做法似乎不需要保证图一直是森林。
另:STL 不不不是有的,但是我不知道,cppref 上也没有写。bitset
似乎没有 clz
/ctz
函数,需要手写 bitset
。
#include<bits/stdc++.h>
using namespace std;
const int N=100005,S=(N>>6)+2,B=320;
#define ull unsigned ll
struct Bs
{
ull s[S];
void set(int p)
{
s[(p-1)>>6]|=1ull<<((p-1)&63);
}
};
int n,q;
Bs* b[N];
VI adj[N];
set<PII>es;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n>>q;
int lans=0;
while(q--)
{
int op,x,y;
cin>>op>>x>>y;
op=1+((op*(lans+1ll)%998244353)&1);
x=1+((x*(lans+1ll)%998244353)%n);
y=1+((y*(lans+1ll)%998244353)%n);
if(op==1)
{
es.emplace(x,y);
if(adj[x].size()>=B)
{
if(adj[x].size()==B)
{
b[x]=new Bs();
for(int i:adj[x])b[x]->set(i);
}
b[x]->set(y);
}
adj[x].PB(y);
if(adj[y].size()>=B)
{
if(adj[y].size()==B)
{
b[y]=new Bs();
for(int i:adj[y])b[y]->set(i);
}
b[y]->set(x);
}
adj[y].PB(x);
}
else
{
if(adj[y].size()<=B)swap(x,y);
if(adj[x].size()<=B)
{
lans=0;
for(int j:adj[x])
if(es.count(minmax(j,y)))
{
lans=j;
break;
}
cout<<lans<<endl;
}
else
{
lans=0;
for(int i=0;i<S;i++)
{
ull t=b[x]->s[i]&b[y]->s[i];
if(t)
{
lans=(i<<6)+__builtin_ctzll(t)+1;
break;
}
}
cout<<lans<<endl;
}
}
}
return 0;
}