AtCoder Beginner Contest 190

AtCoder Beginner Contest 190

不难。

A - Very Very Primitive Game

题意:两个人吃糖,每次吃一颗,\(C=0\)表示第一个人先吃,\(C=1\)表示第二个人先吃,先无糖可吃的人就输了,问谁赢。

简单分类讨论,如果差值大于\(1\)一定是糖多的赢,对\(abs(A-B)=1\)的特殊处理一下即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int main()
{
	int A=read(),B=read(),C=read();
	if(A-B>=2)return puts("Takahashi"),0;
	if(B-A>=2)return puts("Aoki"),0;
	if(C==1&&A>=B)return puts("Takahashi"),0;
	if(C==1&&A<B)return puts("Aoki"),0;
	if(C==0&&B>=A)return puts("Aoki"),0;
	return puts("Takahashi"),0;
}

B - Magic 3

题意:一个人用\(n\)次技能打怪物,第\(i\)个技能触发时间为\(x_i\),威力为\(y_i\),怪物能免疫触发时间大于等于\(S\)技能的和威力小于等于\(D\)的技能,问是否有技能能够打到怪物身上。

按顺序读入,判断是否有满足条件的技能即可(ABC第二题比第一题简单是传统艺能吗

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int n,S,D;
int main()
{
	n=read();S=read();D=read();
	while(n--)
	{
		int x=read(),y=read();
		if(x<S&&y>D)return puts("Yes"),0;
	}
	return puts("No"),0;
}

C - Bowls and Dishes

题意:\(n\)个盘子\(m\)个条件,第\(i\)个条件被满足当且仅当第\(a_i\)个盘子和第\(b_i\)个盘子都不为空。有\(k\)个人过来,第\(i\)个人可以往第\(c_i\)个盘子里放一个球,或往第\(d_i\)个盘子里边放一个球,问最多有多少条件被满足。

感觉应该是有贪心或者其他的做法的,但是这题\(k\)很小,因此直接二进制枚举第\(i\)个人放到第\(c_i\)个中还是第\(d_i\)个中,之后\(O(n)\)统计一下答案即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,k,a[N],b[N],c[N],d[N],vis[101];
int main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		a[i]=read();
		b[i]=read();
	}
	k=read();
	for(int i=1;i<=k;i++)
	{
		c[i]=read();
		d[i]=read();
	}
	int maxn=0;
	for(int i=0;i<(1<<k);i++)
	{
		memset(vis,0,sizeof(vis));
		for(int j=1;j<=k;j++)
		{
			if(i&(1<<(j-1)))vis[c[j]]=1;
			else vis[d[j]]=1;
		}
		int ans=0;
		for(int j=1;j<=m;j++)ans+=(vis[a[j]]&vis[b[j]]);
		maxn=max(maxn,ans);
	}
	printf("%d\n",maxn);
	return 0;
}

D - Staircase Sequences

题意:问和为\(N\)公差为\(1\)的等差数列的个数。

看到等差数列求和就直接列求和公式,设\(x\)为首项,\(k\)为项数,则:

\[\frac{(x+(x+k-1))\times k}{2}=N \]

\(2\)移过去

\[(2x+k-1)\times k=2N \]

那很明显\(k\)就是\(2N\)的一个因子,而一个满足条件的\(k\)需要让\(x\)为正整数,\(O(\sqrt {N})\)枚举\(2N\)的因子即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
#define int long long
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int n,ans;
int check(int k)
{
	if(((n/k)+1-k)%2!=0)return 0;
	return 1;
}
signed main()
{
	n=read()*2ll;
	for(int i=1;i*i<=n;i++)
	{
		if(n%i==0)
		{
			ans+=check(i);
			if(i*i!=n)ans+=check(n/i);
		}
	}
	printf("%lld\n",ans);
	return 0;
}

E - Magical Ornament

题意:有\(N\)种珠子,\(M\)种排列方式,第\(i\)种珠子和第\(j\)种珠子可以挨在一起摆放当且仅当存在\(a_x=i,b_x=j\)\(a_x=j,b_x=i\),现在有\(k\)种珠子要穿在一起,求串的最小长度。

正解好像是\(O(2^KK^2+K(N+M))\)的,我写的是\(O(2^KK^3+K(N+M))\)\(dp+bfs\)

我这个做法还是比较好想的,看到这熟悉的形式,就想到了将\(a_x\)\(b_x\)间连一条无向边,那么取完\(i\)再取\(j\)的最优方式就是在这张图上把\(i\)\(j\)最短路上的珠子都取了。

那首先就可以预处理出来\(dis(i,j)\)表示取完\(i\)再取\(j\)的最少取得数量,由于我们只需要关键点(就是那些要求的珠子)的\(dis\),因此对于每个关键点\(bfs\)一发即可求出\(dis\)

由于\(k\)非常的小,可以用状压\(dp\),设\(f(i,S)\)表示选了\(i\)个珠子集合状态为\(S\)的最小价值,考虑转移时枚举最后一个珠子和倒数第二个珠子。

但是这样做是有问题的,比如说我们现在有\(4\)个珠子,枚举最后一个珠子是\(3\),而倒数第二个珠子是\(2\),那么转移就是从\(f(3,(1011)_2)+dis(2,3)\)转移过来的,但是问题在于:\(f(3,(1011)_2)\)有可能最后一个取得不是\(2\),那么我们就歇逼了。

那考虑直接再加一维表示最后一个取的珠子,即\(f(i,S,j)\)表示取了\(i\)个珠子,关键点集合状态为\(S\),最后一个取的珠子是\(j\),转移直接枚举最后两个取的即可。

答案直接枚举最后一个取的珠子即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define N 100005
#define MAXN 18
#define pb push_back
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,k,f[18][1<<18][18],C[N],dis[18][N],vis[N];
vector<int>G[N];
void bfs(int S)
{
	queue<int>q;
	memset(vis,0,sizeof(vis));
	vis[C[S]]=1;
	dis[S][C[S]]=0;
	q.push(C[S]);
	while(!q.empty())
	{
		int x=q.front();q.pop();int siz=G[x].size();
		for(int i=0;i<siz;i++)
		{
			if(!vis[G[x][i]])
			{
				dis[S][G[x][i]]=dis[S][x]+1;
				vis[G[x][i]]=1;
				q.push(G[x][i]);
			}
		}
	}
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		G[x].pb(y);G[y].pb(x);
	}
	k=read();
	for(int i=1;i<=k;i++)C[i]=read();
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=k;i++)bfs(i);
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=k;i++)f[1][1<<(i-1)][i]=1;
	for(int i=2;i<=k;i++)
	{
		for(int S=0;S<(1<<k);S++)
		{
			for(int j=1;j<=k;j++)
			{
				if(!(S&(1<<(j-1))))continue;
				for(int l=1;l<=k;l++)
				{
					if(!(S&(1<<(l-1))))continue;
					f[i][S][j]=min(f[i][S][j],f[i-1][S-(1<<(j-1))][l]+dis[j][C[l]]);
				}
			}
		}
	}
	int ans=0x3f3f3f3f;
	for(int i=1;i<=k;i++)
	{
		ans=min(ans,f[k][(1<<k)-1][i]);
	}
	printf("%d\n",ans==0x3f3f3f3f?-1:ans);
	return 0;
}

F - Shift and Inversions

题意:一个排列\(a\),定义一个\(a\)的置换\(b\)\(b_i = a_{i+k \bmod N}\),求对于每一个\(k\in[0,N-1]\),所对应的\(b\)的逆序对数量。

说实话没E题难。

首先这个\(b\)就是每一次把\(a\)数组的第一个数取出来放到最后一个。

为了方便,我们先把\(a\)中所有数都加\(1\),让它变成一个排列,方便操作。

那么考虑当前第一个数为\(x\),把他扔到最后一个会有怎样的影响呢?

首先因为第一个数为\(x\),那么\(1 \to x-1\)中所有数一定都在第一个的后边(废话),因此扔到后边逆序对数会减少\(x-1\),但同时因为比\(x\)大的数也都在后边,那会多出来\(n-x\)对逆序对。

因此只需要处理\(k=0\)的情况,之后依次看第一个数即可。

静态序列的逆序对数量是个经典题,树状数组解决即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#define lowbit(x) x&-x
#define N 300005
#define int long long
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int n,a[N],ans;
struct BIT
{
	int c[N];
	void modify(int x,int k){for(;x<=n;x+=lowbit(x))c[x]+=k;}
	int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
}T;
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read()+1;
	for(int i=1;i<=n;i++)
	{
		ans+=i-1-T.query(a[i]);
		T.modify(a[i],1);
	}
	for(int i=1;i<=n;i++)
	{
		printf("%lld\n",ans);
		ans-=(a[i]-1);ans+=n-a[i];
	}
}

感觉不是非常难,下次vp一下ARC。

posted @ 2021-02-09 23:55  shao0320  阅读(119)  评论(0编辑  收藏  举报
****************************************** 页脚Html代码 ******************************************