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
",maxn);
	return 0;
}

D - Staircase Sequences

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

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

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

(2)移过去

[(2x+k-1) imes 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
",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
",ans==0x3f3f3f3f?-1:ans);
	return 0;
}

F - Shift and Inversions

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

说实话没E题难。

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

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

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

首先因为第一个数为(x),那么(1 o 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
",ans);
		ans-=(a[i]-1);ans+=n-a[i];
	}
}

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

原文地址:https://www.cnblogs.com/szmssf/p/14394682.html