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

统计每一个连续的 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;
}

posted @ 2024-11-20 13:04  zhouruoheng  阅读(8)  评论(0编辑  收藏  举报