『模拟赛』暑假集训CSP提高模拟1

Rank

感冒了,同时心情极差,状态不好ww

image

A. Start

打磨你。

T1 放大模拟还是过于抽象了,开局劝退,遂倒序开题。

赛时想复杂了一点,开了两个二维数组存牌,同时存 double 状态时也出了问题,还没有考虑负数的向下取整。我太蒻了

改后虽然还是 300 多行,但是思路起码清晰了很多,改了几个小错就 A 了。

Code:
const int N=3e5+5;
const int mod=1e9+7;
int n,m,k,p,tot=1;
// tot 为牌堆摸取位置
string nam[6],have[6][4],paidui[N];
// 玩家名,玩家拥有手牌,牌堆
bool turnn,doubll,jp,endd;
// 分别表示出牌顺序,double 施加情况,解牌跳过 double,结束回合
// 由于 double 无论如何都是施加在下一个出牌的人身上的,所以一个变量就够
bool cmp(string a,string b)// 无 double 优先级排序
{
	int anum=-1,bnum=-1;
	if(a=="C2") anum=0;
	else if(a=="A99") anum=1;
	else if(a=="A49") anum=2;
	else if(a=="A19") anum=3;
	else if(a=="A9") anum=4;
	else if(a=="A5") anum=5;
	else if(a=="A2") anum=6;
	else if(a=="A1") anum=7;
	else if(a=="B1") anum=8;
	else if(a=="B9") anum=9;
	else if(a=="B19") anum=10;
	else if(a=="D2") anum=11;
	else if(a=="E99") anum=12;
	else if(a=="E49") anum=13;
	else if(a=="E0") anum=14;
	else if(a=="PASS") anum=15;
	else if(a=="TURN") anum=16;
	else if(a=="DOUBLE") anum=17;
	if(b=="C2") bnum=0;
	else if(b=="A99") bnum=1;
	else if(b=="A49") bnum=2;
	else if(b=="A19") bnum=3;
	else if(b=="A9") bnum=4;
	else if(b=="A5") bnum=5;
	else if(b=="A2") bnum=6;
	else if(b=="A1") bnum=7;
	else if(b=="B1") bnum=8;
	else if(b=="B9") bnum=9;
	else if(b=="B19") bnum=10;
	else if(b=="D2") bnum=11;
	else if(b=="E99") bnum=12;
	else if(b=="E49") bnum=13;
	else if(b=="E0") bnum=14;
	else if(b=="PASS") bnum=15;
	else if(b=="TURN") bnum=16;
	else if(b=="DOUBLE") bnum=17;
	return anum<bnum;
}
bool cmpd(string a,string b)// 有 double 优先级排序
{
	int anum=-1,bnum=-1;
	if(a=="PASS") anum=0;
	else if(a=="TURN") anum=1;
	else if(a=="DOUBLE") anum=2;
	else if(a=="D2") anum=3;
	else if(a=="B19") anum=4;
	else if(a=="B9") anum=5;
	else if(a=="B1") anum=6;
	else if(a=="A1") anum=7;
	else if(a=="A2") anum=8;
	else if(a=="A5") anum=9;
	else if(a=="A9") anum=10;
	else if(a=="A19") anum=11;
	else if(a=="A49") anum=12;
	else if(a=="A99") anum=13;
	else if(a=="C2") anum=14;
	else if(a=="E0") anum=15;
	else if(a=="E49") anum=16;
	else if(a=="E99") anum=17;
	if(b=="PASS") bnum=0;
	else if(b=="TURN") bnum=1;
	else if(b=="DOUBLE") bnum=2;
	else if(b=="D2") bnum=3;
	else if(b=="B19") bnum=4;
	else if(b=="B9") bnum=5;
	else if(b=="B1") bnum=6;
	else if(b=="A1") bnum=7;
	else if(b=="A2") bnum=8;
	else if(b=="A5") bnum=9;
	else if(b=="A9") bnum=10;
	else if(b=="A19") bnum=11;
	else if(b=="A49") bnum=12;
	else if(b=="A99") bnum=13;
	else if(b=="C2") bnum=14;
	else if(b=="E0") bnum=15;
	else if(b=="E49") bnum=16;
	else if(b=="E99") bnum=17;
	return anum<bnum;
}
// 排序保证选择普通牌顺序
namespace Wisadel
{
	int Wgetnext(int now)
	{// 找到下一个出牌的人
		if(turnn) now=(now==1?n:now-1);
		else now=(now==n?1:now+1);
		return now;
	}
	void Wworkjp(int now)
	{// 解牌优先
		bool can=0;
		sort(have[now]+1,have[now]+4,cmpd);
		// 按 double 优先级排序
		fo(i,1,3)
		{// 先考虑解牌
			string ss=have[now][i];
			if(ss=="PASS")
			{
				cout<<"used "<<ss<<",now p="<<p<<".\n";
				jp=1;can=1;// 直接跳过
				have[now][i]=paidui[tot++];// 取牌
				break;
			}
			else if(ss=="TURN")
			{
				cout<<"used "<<ss<<",now p="<<p<<".\n";
				jp=1;can=1;
				if(turnn) turnn=0;
				else turnn=1;// 反转出牌顺序
				have[now][i]=paidui[tot++];
				break;
			}
			else if(ss=="DOUBLE")
			{
				cout<<"used "<<ss<<",now p="<<p<<".\n";
				jp=1;can=1;
				doubll=1;// 记录 double 效果
				have[now][i]=paidui[tot++];
				break;
			}
		}
		if(can) return;
		// 若无解牌,则考虑基本牌
		int num=0,no,minn=1e9;
		fo(i,1,3)
		{// 考虑基本牌使用,按要求找最小值
			string ss=have[now][i];no=p;
			if(ss=="A1") no+=1;
			else if(ss=="A2") no+=2;
			else if(ss=="A5") no+=5;
			else if(ss=="A9") no+=9;
			else if(ss=="A19") no+=19;
			else if(ss=="A49") no+=49;
			else if(ss=="A99") no+=99;
			else if(ss=="B1") no-=1;
			else if(ss=="B9") no-=9;
			else if(ss=="B19") no-=19;
			else if(ss=="C2") no*=2;
			else if(ss=="D2") no=floor(1.0*no/2);
			else if(ss=="E0") no=0;
			else if(ss=="E49") no=49;
			else if(ss=="E99") no=99;
			else no=10000;
			if(no<=99&&no<minn) num=i,minn=no,can=1;
		}
		if(!can)
		{// 仍必败
			endd=1;
			cout<<"lost the game.\n";
		}
		else
		{// 可不败,打一摸一
			doubll=0;
			cout<<"used "<<have[now][num]<<",now p="<<minn<<".\n";
			have[now][num]=paidui[tot++];
			p=minn;
		}
	}
	void Wwork(int now)
	{// 普通牌优先
		sort(have[now]+1,have[now]+4,cmp);
		// 按无 double 优先级排序
		bool can=0;int num=0,no,maxx=-1e9;
		fo(i,1,3)
		{// 考虑基本牌使用,按要求找范围内最大值
			string ss=have[now][i];no=p;
			if(ss=="A1") no+=1;
			else if(ss=="A2") no+=2;
			else if(ss=="A5") no+=5;
			else if(ss=="A9") no+=9;
			else if(ss=="A19") no+=19;
			else if(ss=="A49") no+=49;
			else if(ss=="A99") no+=99;
			else if(ss=="B1") no-=1;
			else if(ss=="B9") no-=9;
			else if(ss=="B19") no-=19;
			else if(ss=="C2") no*=2;
			else if(ss=="D2") no=floor(1.0*no/2);
			else if(ss=="E0") no=0;
			else if(ss=="E49") no=49;
			else if(ss=="E99") no=99;
			else no=10000;
			if(no<=99&&no>maxx) num=i,maxx=no,can=1;
		}
		if(can)
		{// 能打则打一摸一
			cout<<"used "<<have[now][num]<<",now p="<<maxx<<".\n";
			p=maxx;
			have[now][num]=paidui[tot++];
			return;
		}
		fo(i,1,3)
		{// 否则考虑解牌
			string ss=have[now][i];
			if(ss=="PASS")
			{
				cout<<"used "<<ss<<",now p="<<p<<".\n";
				can=1;
				have[now][i]=paidui[tot++];
				break;
			}
			else if(ss=="TURN")
			{
				cout<<"used "<<ss<<",now p="<<p<<".\n";
				can=1;
				if(turnn) turnn=0;
				else turnn=1;
				have[now][i]=paidui[tot++];
				break;
			}
			else if(ss=="DOUBLE")
			{
				cout<<"used "<<ss<<",now p="<<p<<".\n";
				can=1;doubll=1;
				have[now][i]=paidui[tot++];
				break;
			}// 过程中直接打一摸一
		}
		if(!can)
		{// 必败
			endd=1;
			cout<<"lost the game.\n";
		}
	}
	short main()
	{
		// freopen("A.in","r",stdin),freopen("1.out","w",stdout);
		n=qr,m=qr,k=qr;
		fo(i,1,n) cin>>nam[i]>>have[i][1]>>have[i][2]>>have[i][3];
		fo(i,1,k) cin>>paidui[i];
		// 输入
		int laslos=1;
		// 从上回合熟的人开始,第一回合从 1 号开始
		fo(roundd,1,m)
		{
			printf("Round %d:\n",roundd);
			doubll=p=turnn=endd=0;int now=laslos;
			// 初始化标记变量
			while(1)
			{
				cout<<nam[now]<<' ';jp=0;
				if(doubll)
				{// 有 double 效果
					Wworkjp(now);
					// 先按 double 打一次
					if(jp)
					{// 使用了解牌,直接跳过
						now=Wgetnext(now);
						continue;
					}
					else if(endd)
					{// 败了
						laslos=now;
						if(roundd!=m)
						{// 重新摸牌
							have[now][1]=paidui[tot++];
							have[now][2]=paidui[tot++];
							have[now][3]=paidui[tot++];
						}
						break;
					}
					else 
					{// 否则按无 double 效果再进行一回合
						cout<<nam[now]<<' ';
						Wwork(now);
					}
				}
				else Wwork(now);
				// 无 double 效果
				if(endd)
				{// 败了
					laslos=now;
					if(roundd!=m)
					{
						have[now][1]=paidui[tot++];
						have[now][2]=paidui[tot++];
						have[now][3]=paidui[tot++];
					}
					break;
				}
				now=Wgetnext(now);
			}
		}
		return Ratio;
	}
}
int main(){return Wisadel::main();}

确实又臭又长了属于是。

咕一个长度在 150 行以内的,不无脑压行。


B. Mine

一个简单的 dp,但赛时倒数开的,时间不是很足,在暴搜上耽误很久。

所以以后看见题不能光想着搜了,脑子浑了就打醒之后考虑考虑 dp。

思路挺简单,讲的也很明白,不多写了。

C. 小凯的疑惑

数论题。同样最后开的这道题,压根没去想正解,要不是打 Subtask1 时候暴力摸出来了 Subtask2,这题就保龄了。

其实不互质无解挺显然的,但赛时没看出来,唐了。

然后远古的题考察化简式子化成 \(O(1)\) 的,但由于评测机的更新换代现在 \(10^8\) 的范围 \(O(n)\) 也能过,但还是没想出来。

image

粘一张推导,以后看。

D. 春节十二响

其实一眼泄题了不是吗

一道启发式合并。

题面挺简单的,起码看了 T1 之后这么想。

首先想的是树剖,每条链按类似田忌赛马的思想,最大值放在一起,次大值放在一起,以此类推,答案即为每一堆中最大值的和。

那么发现它是一种合并的思想,最后结果是得到了一条链,答案也就变成了这条链上的点权之和。

实现也不难,把每个父节点向子节点连一条单向边,dfs 到最深然后逐层合并,每条链的头没有对应的点,合并后再加入,链用优先队列维护很方便。

Code:
const int N=4e6+5;
int n;
ll ans;
int a[N],zc[N],id[N],tot;
int hh[N],to[N],ne[N],cnt;
priority_queue<int>q[N];
namespace Wisadel
{
	void Wadd(int u,int v)
	{
		to[++cnt]=v;
		ne[cnt]=hh[u];
		hh[u]=cnt;
	}
	void Wmerge(int x,int y)
	{
		if(q[id[x]].size()<q[id[y]].size()) swap(id[x],id[y]);
		int len=q[id[y]].size();
		fo(i,1,len)
			zc[i]=max(q[id[x]].top(),q[id[y]].top()),
			q[id[x]].pop(),q[id[y]].pop();
		fo(i,1,len) q[id[x]].push(zc[i]);
	}
	void Wdfs(int u)
	{
		id[u]=++tot;
		for(int i=hh[u];i!=-1;i=ne[i])
		{
			int v=to[i];
			Wdfs(v);
			Wmerge(u,v);
		}
		q[id[u]].push(a[u]);
	}
	short main()
	{
		memset(hh,-1,sizeof hh);
		n=qr;int f;
		fo(i,1,n) a[i]=qr;
		fo(i,2,n) f=qr,Wadd(f,i);
		Wdfs(1);
		while(q[id[1]].size()) ans+=q[id[1]].top(),q[id[1]].pop();
		printf("%lld\n",ans);
		return Ratio;
	}
}
int main(){return Wisadel::main();}

完结撒花~

久违了

posted @ 2024-07-18 19:54  DrRatio  阅读(33)  评论(0编辑  收藏  举报