AtCoder Beginner Contest 380
AtCoder Beginner Contest 380 总结
A
用桶统计 \(1\),\(2\),\(3\) 出现的次数,判断即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=10;
string s;
int c[N];
void solve()
{
cin>>s;
for(auto x:s) c[x-'0']++;
if(c[1]==1&&c[2]==2&&c[3]==3) cout<<"Yes\n";
else cout<<"No\n";
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
B
扫一遍统计即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=105;
string s;
int n;
int a[N];
void solve()
{
cin>>s;
int cnt=0;
for(int i=0;i<s.size();i++)
{
if(s[i]=='|') a[n++]=cnt,cnt=0;
else cnt++;
}
for(int i=1;i<n;i++) cout<<a[i]<<' ';
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
C
统计每一个连续的 0
或 1
段的大小,找到第 \(k\) 个 1
段和前一个 0
段交换。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,k;
string s;
int a[N],c[N];
void solve()
{
cin>>n>>k>>s;
int cnt=0;
s=' '+s;
for(int i=1;i<s.size();i++)
{
int x=s[i]-'0';
if(s[i]!=s[i-1]) a[++cnt]=x,c[cnt]++;
else c[cnt]++;
}
int j=0;
for(int i=1;i<=cnt;i++)
{
if(a[i]==1) j++;
if(j==k)
{
swap(a[i],a[i-1]);
swap(c[i],c[i-1]);
break;
}
}
for(int i=1;i<=cnt;i++)
for(int j=1;j<=c[i];j++)
cout<<a[i];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
D
可以知道最终的 \(S\) 是由若干个翻转或不变的初始的 \(S\) 拼成,用 \(1\) 表示翻转,\(0\) 表示不变。最终会是 \(\mathtt{0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, \dots}\)
考虑找 \(k\) 在第几个 \(s\) 中,记为 \(a\),因为复制的过程是不断加倍,思考一下可以得到 \(a\) 是由 \(a-\text{二进制最高位的一代表的权值}\) 推过来的,且翻转了。考虑倒推发现最终会剩下 \(lowbit(a)=2^c\),观察下发现对应翻转情况为 c&1
,统计 \(a\) 二进制下 \(1\) 的个数为 \(b\),也就是要翻转 \(b-1\) 次。所以 \(a\) 对应的翻转情况为 (c&1)^((b-1)&1)
。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=2e5+5;
string s;
int n,q;
char change(char x)
{
if(x>='a'&&x<='z') x=x-'a'+'A';
else x=x-'A'+'a';
return x;
}
int count(ll x)
{
ll y=x&-x;
ll a=1,c=0;
while(a<y)
{
a*=2;
c++;
}
ll b=0;
while(x) x-=(x&-x),b++;
b=(b-1)&1;
return c&1^b;
}
void solve()
{
cin>>s>>q;
n=s.size();
s=s[n-1]+s;
while(q--)
{
ll k;
cin>>k;
ll a=k/n,b=k%n;
if(b==0)
{
if(count(a)) cout<<change(s[n])<<' ';
else cout<<s[n]<<' ';
continue;
}
if(count(a+1)) cout<<change(s[b])<<' ';
else cout<<s[b]<<' ';
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
E
显然是用并查集来维护,注意一下改变完某一块后是否与左右块的颜色相同,如果相同则要合并。所以记录集合的颜色,左右端点,大小。要注意答案可能不只是一个集合的大小,所以要另外计算。
只要不看错题目还是挺简单的 qwq。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,q;
int a[N],l[N],r[N],fa[N],siz[N],ans[N];
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return ;
fa[x]=y;
siz[y]+=siz[x];
}
void solve()
{
cin>>n>>q;
for(int i=1;i<=n;i++) a[i]=l[i]=r[i]=fa[i]=i,siz[i]=ans[i]=1;
while(q--)
{
int op,x,c;
cin>>op>>x;
if(op==1)
{
cin>>c;
x=find(x);
ans[a[x]]-=siz[x];
a[x]=c;
ans[a[x]]+=siz[x];
if(l[x]!=1&&a[find(l[x]-1)]==c)
{
int y=find(l[x]-1);
merge(y,x),l[x]=l[y];
}
if(r[x]!=n&&a[find(r[x]+1)]==c)
{
int y=find(r[x]+1);
merge(y,x),r[x]=r[y];
}
}
else cout<<ans[x]<<'\n';
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
F
因为总牌数比较小,所以不妨使用状态压缩,记录每张牌在谁哪,三进制状态,\(0\) 表示在 Takahashi 那,\(1\) 表示在 Aoki 那,\(2\) 表示在桌上。直接按题意模拟,使用记忆化搜索即可。
要注意三进制状态的处理。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=15,M=6e6;
int n,m,l;
int a[N],p[N];
int f[2][M];
int count(int x,int st)
{
int cnt=0;
for(int i=0;i<n+m+l;i++)
{
if(st%3==x) cnt++;
st/=3;
}
return cnt;
}
int get(int st,int k)
{
int ret=0;
for(int i=0;i<=k;i++)
{
ret=st%3;
st/=3;
}
return ret;
}
bool dfs(int x,int st)
{
if(f[x][st]!=-1) return f[x][st];
if(count(x,st)==0) return f[x][st]=0;
f[x][st]=0;
for(int i=0;i<n+m+l;i++)
if(get(st,i)==x)
{
f[x][st]|=!dfs(x^1,st-x*p[i]+2*p[i]);
for(int j=0;j<n+m+l;j++)
if(a[j]<a[i]&&get(st,j)==2)
f[x][st]|=!dfs(x^1,st-x*p[i]+2*p[i]+x*p[j]-2*p[j]);
}
return f[x][st];
}
void solve()
{
cin>>n>>m>>l;
p[0]=1;
int st=0;
for(int i=1;i<n+m+l;i++) p[i]=p[i-1]*3;
for(int i=n;i<n+m;i++) st+=p[i];
for(int i=n+m;i<n+m+l;i++) st+=2*p[i];
for(int i=0;i<n+m+l;i++) cin>>a[i];
memset(f,-1,sizeof f);
if(dfs(0,st)) cout<<"Takahashi\n";
else cout<<"Aoki\n";
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
G
将序列划分成三个区间 \([1,i-1]\),\([i,i+k-1]\),\([i+k,n]\)。
其中 \([i,i+k-1]\) 随机打乱。因为是求逆序对,被影响的只用 \([i,i+k-1]\) 内产生的逆序对。考虑 \(k\) 个数随机打乱产生逆序对数量的期望。总共 \(\frac{k \times (k-1)}{2}\) 对数,每对数是逆序对的概率为 \(\frac{1}{2}\),所以该区间产生的期望是 \(\frac{k \times (k-1)}{4}\)。
再来计算其他部分。设整个序列的逆序对数为 \(sum\),\([i,i+k-1]\) 中产生的逆序对数为 \(now\),所以其他部分的逆序对数为 \(sum-now\)。整个序列的期望就是 \(sum-now+\frac{k \times (k-1)}{4}\)。区间平移后,要减去 \(a_{i-1}\) 加入 \(a_{i+k-1}\),分别求出该区间小于 \(a_{i-1}\) 的数的个数 \(x\),和大于 \(a_{i+k-1}\) $的数的个数 \(y\),将 \(now\) 更新为 \(now-x+y\)。
类似与滑动窗口求逆序对,用树状数组来写。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#define int long long
using namespace std;
typedef long long ll;
const int N=2e5+5,mod=998244353;
int n,k;
int a[N];
struct BIT
{
int c[N];
int lowbit(int x) {return x&-x;}
void clear() {memset(c,0,sizeof c);}
void add(int k,int x) {for(int i=k;i<=n;i+=lowbit(i)) c[i]+=x;}
int query(int k) {int ret=0;for(int i=k;i;i-=lowbit(i)) ret+=c[i];return ret;}
int ask(int k) {return query(n)-query(k);}
}T;
int inv(int x)
{
int ret=1,y=mod-2;
while(y)
{
if(y&1) ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
void solve()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
int sum=0,ans=0,now=0;
for(int i=1;i<=n;i++) sum+=T.ask(a[i]),T.add(a[i],1);
T.clear();
for(int i=1;i<=k;i++) now+=T.ask(a[i]),T.add(a[i],1);
ans=sum-now;
for(int i=2;i<=n-k+1;i++)
{
T.add(a[i-1],-1);
now-=T.query(a[i-1]);
now+=T.ask(a[i+k-1]);
T.add(a[i+k-1],1);
ans=(ans+sum-now)%mod;
}
cout<<(ans*inv(n-k+1)%mod+k*(k-1)%mod*inv(4))%mod;
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}