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;
}