AtCoder Beginner Contest 380

zhouruoheng發表於2024-11-20

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

統計每一個連續的 01 段的大小,找到第 \(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;
}

相關文章