A. Tokitsukaze and Enhancement

**题意:**Tokitsukaze的HP值有 44 种类别:

  1. HP =(4n+1)\ =(4n+1),即HP值除以 44,余 11,为 AA 类;
  2. HP =(4n+2)\ =(4n+2),即HP值除以 44,余 22,为 CC 类;
  3. HP =(4n+3)\ =(4n+3),即HP值除以 44,余 33,为 BB 类;
  4. HP =4n\ =4n,即HP值除以 44,余 00,为 DD 类。

nn 可以是任何整数。

44 种类别优先度的排序为 A>B>C>DA>B>C>D,这意味着 AA 是最好的,DD 是最坏的。

玩游戏时,玩家可以增加角色的HP,现在Tokitsukaze希望你最多增加她的HP值为 22(也就是说可以加 001122)。她应该增加多少HP以使其具有最高级别。

**输入:**只有一个整数 xx,表示目前Tokitsukaze的HP值。

**输出:**一个整数 aa,和一个大写字母 bbaa 表示增加的HP值,bb 表示增加HP值后她的HP类别。

**解析:**很简单的一道模拟,按照题目推就可以了。

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
char a,b,c;
char min(char a,char b)
{
	if(a<b)
		return a;
	return b;
}
char solve(int x)
{
	if(x%4==1)
		return 'A';
	else if(x%4==2)
		return 'C';
	else if(x%4==3)
		return 'B';
	else
		return 'D';
}
int x;
int main()
{
	scanf("%d",&x);
	a=solve(x);
	b=solve(x+1);
	c=solve(x+2);
	char ans=min(a,min(b,c));
	if(ans==a)
		printf("0 %c",ans);
	else if(ans==b)
		printf("1 %c",ans);
	else
		printf("2 %c",ans);
    return 0;
}

B. Tokitsukaze and Mahjong

**题意:**Tokitsukaze在玩日本麻将,在游戏中,她有三张牌,每一张牌都由一个字母和一个数字组成,数字范围从 191\sim9,字母为 ssmmpp

为了赢这场比赛,她必须达成以下胜利条件之一:

  1. 手中有三个完全相同的牌,如 [1m,1m,1m][1m,1m,1m],但是 [1m,1p,1s][1m,1p,1s][1m,4m,7m][1m,4m,7m] 则不行。
  2. 手中有一组序列,由同一字母的三个连续编号的牌组成,如 [1m,2m,3m][1m,2m,3m][5s,7s,6s][5s,7s,6s],但是 [9m,1m,2m][9m,1m,2m][1m,2p,3s][1m,2p,3s] 则不行。

如果她没有达成胜利条件,她就应该画出更多适合自己的牌,她可以画出她想要的任何牌,包括她手中的牌,并且她可以绘制每种牌的数量是无限的。

**输入:**一行,三个字符串,表示Tokitsukaze手中的牌。

**输出:**一个整数,表示她需要绘制最小数量来达成胜利条件的牌的数量。

**解析:**依然很简单的一道模拟,计算牌中不同字母类别的数量,在根据数量分类讨论达成最小数量的牌即可,答案只可能为 0,1,20,1,2

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int flag[4],maxx=0,maxi;
struct mahjong
{
	int a;
	char b;
}s[4];
int cmp(mahjong x,mahjong y)
{
	return x.a<y.a;
}
void calc(char p)
{
	if(p=='s')
		flag[1]++;
	else if(p=='m')
		flag[2]++;
	else
		flag[3]++;
}
int main()
{
	for(int i=1;i<=3;i++)
	{
		scanf("%d",&s[i].a);
		scanf("%c",&s[i].b);
		calc(s[i].b);
		getchar();
	}
	for(int i=1;i<=3;i++)
	{
		if(flag[i]>maxx)
		{
			maxx=flag[i];
			maxi=i;
		}
	}
	sort(s+1,s+4,cmp);
	if(maxx==3)
	{
		if(s[1].a==s[2].a&&s[1].a==s[3].a)
		{
			printf("0");
			return 0;
		}
		if(s[1].a+1==s[2].a&&s[2].a+1==s[3].a)
		{
			printf("0");
			return 0;
		}
		else
		{
			if(s[1].a==s[2].a||s[1].a==s[3].a||s[2].a==s[3].a)
			{
				printf("1");
				return 0;
			}
			if(s[1].a+1==s[2].a||s[2].a+1==s[3].a||s[1].a+2==s[2].a||s[2].a+2==s[3].a)
			{
				printf("1");
				return 0;
			}
			printf("2");
			return 0;
		}
	}
	else if(maxx==2)
	{
		char t;
		if(maxi==1)
			t='s';
		else if(maxi==2)
			t='m';
		else
			t='p';
		if(s[1].b==t&&s[2].b==t)
			if(s[1].a==s[2].a||s[1].a+1==s[2].a||s[1].a+2==s[2].a)
			{
				printf("1");
				return 0;
			}
		if(s[1].b==t&&s[3].b==t)
			if(s[1].a==s[3].a||s[1].a+1==s[3].a||s[1].a+2==s[3].a)
			{
				printf("1");
				return 0;
			}
		if(s[2].b==t&&s[3].b==t)
			if(s[2].a==s[3].a||s[2].a+1==s[3].a||s[2].a+2==s[3].a)
			{
				printf("1");
				return 0;
			}
		printf("2");
		return 0;
	}
	else
	{
		printf("2");
		return 0;
	}
    return 0;
}

C. Tokitsukaze and Discard Items

**题意:**有一个长度为 nn 的序列,每 kk 个为一段,有 mm 个特殊元素需要取走,一段一段过来,每次取走当前段中所有特殊元素,然后后面的元素前移,继续做这个操作,直到所有特殊元素被取走。

过程如图所示:

**输入:**两行,第一行三个整数,n,m,kn,m,k;第二行 mm 个整数,代表 mm 个特殊元素的位置。

**输出:**一个整数,代表操作次数。

**解析:**枚举一编,用 lazylazy 维护当前元素移动后的下标,然后判断以下相邻特殊元素是否在同一段,如果在同一段就一起取掉,更新 lazylazy

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
long long n,m,k,a[101010];
int main()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	for(int i=1;i<=m;i++)
		scanf("%lld",&a[i]);
	long long lazy=0;
	long long ans=1;
	long long num=1;
	for(int i=2;i<=m;i++)
	{
		if((a[i]-1-lazy)/k!=(a[i-1]-1-lazy)/k)
		{
			ans++;
			lazy+=num;
			num=1;
		}
		else
			num++;
	}
	printf("%lld",ans);
    return 0;
}

D. Tokitsukaze, CSL and Stone Game

**题意:**有 nn 堆石头,每次只能从一堆中取走一个。
失败条件:

  • 当前没有石头可取。
  • 取走一颗石头后,存在两堆石头个数相同。
  • 如果开始的时候有两堆石头相同是没有关系的,只有操作之后还有两堆石头相同才会失败。

**输入:**两行,第一行一个整数 nn,代表石头有多少堆;第二行 nn 个整数,代表每堆石头数。

**输出:**如果先手胜,输出”sjfnbsjfnb”,否则输出”cslnbcslnb”。

**解析:**显然取完后最后的石头个数会是从 00 开始的依次递增数列,那么可以取的石头个数是固定的。如果刚开始有两堆及以上为空,那么先手必败。如果有且只有两堆石头个数相同,假设个数为 xx,并且也存在 x1x−1 这堆石头,那么先手必败。如果刚开始有两堆以上石头个数相同或者又若干个两堆数量相同的石头,那么先手必败。

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,a[101010];
map<int,int> s;
map<int,int>::iterator it;
bool judge()
{
	s.clear();
	for(int i=1;i<=n;i++)
	{
		s[a[i]]++;
		if(s[a[i]]>2)//如果一开局就有大于两堆石头数目相同,先手必输
			return false;
	}
	int cnt=0;
	for(it=s.begin();it!=s.end();it++)
		if(it->second==2)//判断石头数目为此数量的堆数是否有两堆
			++cnt;//记录有相同数目的两堆石头的出现有多少组
	if(cnt==1)//如果整个游戏中只出现了一组有相同数目的两堆石头
		for(it=s.begin();it!=s.end();it++)
			if(it->second==2)
				if(it->first==0||s.find(it->first-1)!=s.end())//设这两堆石头数目为i,如果i=0或者整个游戏中存在数目为i-1的石头堆,即从这两堆中取出1个石头还是会与其他石头数目重复
					return false;//先手必输
	return cnt<=1;//如果大于1组则先手必输
}
int main()
{
	long long sum=0,tot=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum+=a[i];//sum为现在的总石头数
		tot+=(i-1);//如果要不重复,则要把石头变成0,1,2,3……n-1这样,tot为这种状态的总石头数
	}
	if(sum==0||!judge())//如果总石头数为0或者已经判断先手必输
		printf("cslnb");
	else
	{
		long long p=sum-tot;//p为从目前状态达到目标状态需要取出的石子数
		if(p&1)//如果需要取出奇数颗,先手必胜
			printf("sjfnb");
		else
			printf("cslnb");
	}
    return 0;
}

E. Tokitsukaze and Duel

**题意:**有一个 0101 串,每次可以选择连续的 kk 个,全都变成 00 或者全都变成 11,两个人轮流行动,谁先把这个串变成全 00 或者变成全 11,那么谁胜利,或者判定是否是平局。

**输入:**两行,第一行两个整数 nnkk;第二行一个字符串,为 0101 串。

**输出:**如果先手胜,输出”tokitsukazetokitsukaze”,否则输出”quailtyquailty”。平局则输出”once againonce\ again”。

**解析:**考虑一个先手必胜的情况, 即它可以通过第一步操作就把该串变成全 00 或者全 11,那么先手必胜。考虑一个先手必败的情况,即先手第一步无论怎么操作,后手都能在第二步将该串变成全 00 或者全 11,那么先手必败。那么除了这两种情况,接下来的什么情况呢? 是平局。可以这么考虑,如果刚开始的情况不是先手必胜的,那么先手肯定想让局势走平局,也就是说它的这一步操作不会达到先手必败的状态。既然不会到达先手必败的状态,那么对手面对的也是同样一个局面,它也会让局势走向平局。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,k;
char s[101010];
int sum[101010];//前缀和,可快速通过求一段字符的和判断这段字符是否全为0或1(全为0则和为0,全为1则为这段长度)
bool judge1()//判断能否让先手必胜
{
	if(n==k)//如果字符串长度正好为k,先手必胜
		return 1;
	for(int i=1;i+k-1<=n;i++)//枚举长度为k的字符串的起点
	{
		int l=i;
		int r=i+k-1;//起点和终点位置
		int len1=l-1;//起点离开头距离
		int len2=n-r;//终点离结尾距离
		int L=sum[l-1];//开头到起点的和
		int R=sum[n]-sum[r];//终点到结尾的和
		if(l==1&&(R==0||R==len2))//特判,如果起点在开头,且终点到结尾的和全为0(R==0)或1(R==len2
			return 1;
		if(r==n&&(L==0||L==len1))//特判,同上,终点在末尾
			return 1;
		if((len1==L&&len2==R)||(L==0&&R==0))//普通情况,除这段长度为k的字符外其他字符全为0或1
			return 1;
	}
	return 0;
}
bool judge2()//判断能否平局
{
	if(k<(n+1)/2)//如果k小于n的一半,那么如果先手不能必胜,先手可以改变不同字符的另一半位置,让后手不能胜利
		return 1;
	for(int i=2;i+k-1<n;i++)
	{
		int l=i;
		int r=i+k-1;
		int len1=l-1;
		int len2=n-r;
		int L=sum[l-1];
		int R=sum[n]-sum[r];
		if((L&&L!=len1)||(R&&R!=len2))//如果有一边不全为0且不全为1
			return 1;//平局
	}
	return 0;
}
int main()
{
	scanf("%d%d",&n,&k);
	scanf("%s",s+1);
	sum[0]=0;
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]-'0'+s[i];
	if(judge1())
		printf("tokitsukaze");
	else if(judge2())
		printf("once again");
	else
		printf("quailty");
    return 0;
}

解析中引用了一些这位大佬的思路解析,在此表示感谢。