暑假集训CSP提高模拟1

暑假集训CSP提高模拟1

组题人: @Delov

\(T1\) T2687. Start

  • 原题: luogu P7506 「Wdsr-2.5」琪露诺的算数游戏

  • 大模拟。

  • 需要注意的地方。

    • 整数除法是向零取整,右移运算是向下取整,处理负数时会有差异,建议使用 floor(...)
    • 给定的牌的顺序是从上至下,若要形成一个栈的话需要反向加入。
    • 打完一张牌后迅速摸牌。
    • 处于 \(\tt{DOUBLE}\) 状态下打出的第二张牌不再受 \(\tt{DOUBLE}\) 状态限制。
    • 判断普通牌时不要把解牌一并算入。
    点击查看代码
    struct people
    {
    	string name;
    	map<string,int>card;
    }a[40];
    string ls;
    deque<string>s;
    map<string,int>::iterator it;
    int card_to(string s,int p)
    {
    	if(s=="A1"){return p+1;}
    	if(s=="A2"){return p+2;}
    	if(s=="A5"){return p+5;}
    	if(s=="A9"){return p+9;}
    	if(s=="A19"){return p+19;}
    	if(s=="A49"){return p+49;}
    	if(s=="A99"){return p+99;}
    	if(s=="B1"){return p-1;}
    	if(s=="B9"){return p-9;}
    	if(s=="B19"){return p-19;}
    	if(s=="C2"){return p*2;}
    	if(s=="D2"){return floor(1.0*p/2);}
    	if(s=="E0"){return 0;}
    	if(s=="E49"){return 49;}
    	if(s=="E99"){return 99;}
    	return 114514;
    }
    int card_in(string s,int flag)
    {
    	if(s=="PASS"||s=="TURN"||s=="DOUBLE"){return -1;}
    	if(s[0]=='A'){return (flag==0)?2:3;}
    	if(s[0]=='B'){return (flag==0)?3:2;}
    	if(s[0]=='C'){return (flag==0)?1:4;}
    	if(s[0]=='D'){return (flag==0)?4:1;}
    	if(s[0]=='E'){return (flag==0)?5:5;}
    	return 114514;
    }
    int check_exist(int pos,string s)
    {
    	return a[pos].card[s]>=1;
    }
    int del_card(int pos,string s,int p)
    {
    	if(check_exist(pos,s)==0)
    	{
    		return 0;
    	}
    	else
    	{
    		a[pos].card[s]--;
    		cout<<a[pos].name<<" used "<<s<<",now p="<<p<<"."<<endl;
    		return 1;	
    	}
    }
    int basic_card(int pos,int &p,int flag)
    {
    	int sum=(flag==0)?-0x7f7f7f7f:0x7f7f7f7f;
    	string ans;
    	if(flag==0)
    	{
    		for(it=a[pos].card.begin();it!=a[pos].card.end();it++)
    		{	
    			if(check_exist(pos,it->first)==1&&card_in(it->first,flag)!=-1)
    			{
    				if(sum<card_to(it->first,p)&&card_to(it->first,p)<=99)
    				{
    					sum=card_to(it->first,p);
    					ans=it->first;
    				}
    				else
    				{
    					if(card_to(it->first,p)==sum&&card_in(ans,flag)>card_in(it->first,flag))
    					{
    						ans=it->first;
    					}
    				}
    			}
    		}
    	}
    	else
    	{
    		for(it=a[pos].card.begin();it!=a[pos].card.end();it++)
    		{	
    			if(check_exist(pos,it->first)==1&&card_in(it->first,flag)!=-1)
    			{
    				if(sum>card_to(it->first,p)&&card_to(it->first,p)<=99)
    				{
    					sum=card_to(it->first,p);
    					ans=it->first;
    				}
    				else
    				{
    					if(card_to(it->first,p)==sum&&card_in(ans,flag)>card_in(it->first,flag))
    					{
    						ans=it->first;
    					}
    				}
    			}
    		}
    	}
    	if(sum!=((flag==0)?-0x7f7f7f7f:0x7f7f7f7f))
    	{
    		p=sum;
    		del_card(pos,ans,p);
    		return 1;
    	}
    	else
    	{
    		return 0;
    	}
    }
    int resolve_card(int pos,int p,int &dir,int &flag)	
    {
    	if(del_card(pos,"PASS",p)==1){return 1;}
    	if(del_card(pos,"TURN",p)==1){dir^=1;return 1;}
    	if(del_card(pos,"DOUBLE",p)==1){flag=1;return 1;}
    	return 0;
    }
    void get_card(int pos)
    {
    	a[pos].card[s.front()]++;
    	s.pop_front();	
    }
    int main()
    {
    	int n,m,k,p,flag,dir,pos=1,i,j;
    	cin>>n>>m>>k;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].name;
    		for(j=1;j<=3;j++)
    		{
    			cin>>ls;
    			a[i].card[ls]++;
    		}
    	}
    	for(i=1;i<=k;i++)
    	{
    		cin>>ls;
    		s.push_back(ls);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cout<<"Round "<<i<<":"<<endl;
    		p=flag=0;
    		dir=1;
    		while(1)
    		{
    			if(flag==0)
    			{	
    				if(basic_card(pos,p,flag)==0&&resolve_card(pos,p,dir,flag)==0)
    				{
    					break;
    				}
    				else
    				{
    					get_card(pos);	
    				}
    			}
    			else
    			{
    				if(resolve_card(pos,p,dir,flag)==0)
    				{
    					if(basic_card(pos,p,flag)==0)
    					{
    						break;
    					}
    					else
    					{
    						flag=0;
    						get_card(pos);
    						if(basic_card(pos,p,flag)==0)
    						{
    							if(resolve_card(pos,p,dir,flag)==0)
    							{
    								break;
    							}
    							else
    							{
    								get_card(pos);
    							}
    						}
    						else
    						{
    							flag=0;
    							get_card(pos);
    						}
    					}
    				}
    				else
    				{
    					get_card(pos);
    				}
    			}
    			if(dir==1)
    			{
    				pos++;
    				if(pos==n+1)
    				{
    					pos=1;
    				}
    			}
    			else
    			{
    				pos--;
    				if(pos==0)
    				{
    					pos=n;
    				}
    			}
    		}
    		cout<<a[pos].name<<" lost the game."<<endl;
    		a[pos].card.clear();
    		for(j=1;j<=3;j++)
    		{
    			a[pos].card[s.front()]++;
    			s.pop_front();
    		}
    	}
    	return 0;
    }
    

\(T2\) T807. mine

  • 原题: CF404D Minesweeper 1D

  • \(f_{i,0/1/2/3/4}\) 分别表示处理到第 \(i\) 位时,第 \(i\) 位为雷/第 \(i\) 位不为雷,第 \(i-1,i+1\) 位不为雷/第 \(i\) 位不为雷,第 \(i-1\) 位不为雷,第 \(i+1\) 位为雷/第 \(i\) 位不为雷,第 \(i-1\) 位为雷,第 \(i+1\) 位不为雷/第 \(i\) 位不为雷,第 \(i-1,i+1\) 位为雷的方案数,状态转移方程(如果存在这个状态的话)为 \(\begin{cases} f_{i,0}=f_{i-1,0}+f_{i-1,2}+f_{i-1,4} \\ f_{i,1}=f_{i-1,1}+f_{i-1,3} \\ f_{i,2}=f_{i-1,1}+f_{i-1,3} \\ f_{i,3}=f_{i-1,0} \\ f_{i,4}=f_{i-1,0} \end{cases}\) ,边界为 \(f_{0,1}=f_{0,2}=1\)

  • 最终,有 \(f_{|s|,0}+f_{|s|,1}+f_{|s|,3}\) 即为所求。

    点击查看代码
    const ll p=1000000007;
    ll f[1000010][5];
    char s[1000010];
    int main()
    {
    	ll n,i;
    	cin>>(s+1);
    	n=strlen(s+1);
    	f[0][1]=f[0][2]=1;
    	for(i=1;i<=n;i++)
    	{
    		if(s[i]=='?')
    		{
    			f[i][0]=(f[i-1][0]+f[i-1][2]+f[i-1][4])%p;
    			f[i][1]=(f[i-1][1]+f[i-1][3])%p;
    			f[i][2]=(f[i-1][1]+f[i-1][3])%p;
    			f[i][3]=f[i-1][0];
    			f[i][4]=f[i-1][0];
    		}
    		if(s[i]=='*')
    		{
    			f[i][0]=(f[i-1][0]+f[i-1][2]+f[i-1][4])%p;
    		}
    		if(s[i]=='0')
    		{
    			f[i][1]=(f[i-1][1]+f[i-1][3])%p;
    		}
    		if(s[i]=='1')
    		{
    			f[i][2]=(f[i-1][1]+f[i-1][3])%p;
    			f[i][3]=f[i-1][0];
    		}
    		if(s[i]=='2')
    		{
    			f[i][4]=f[i-1][0];
    		}
    	}
    	cout<<(f[n][0]+f[n][1]+f[n][3])%p<<endl;
    	return 0;
    }
    
  • 貌似还有列出方程组后,手动解带状矩阵,求自由元数量的高级做法,但我不会,暂时咕了。

\(T3\) T2790. 小凯的疑惑

  • 最终能组成的数一定能表示成 \(k \times \gcd(x,y)(k \in \mathbb{N})\) 的形式,即最终能组成的数 \(\bmod \gcd(x,y)=0\)

  • \(d \ne 1\) 时存在无数个正整数不能被如此表示。

  • \(d=1\)

    • 由弱化版 luogu P3951 [NOIP2017 提高组] 小凯的疑惑 / [蓝桥杯 2013 省] 买不到的数目 的结论,有不能被 \(x,y\) 表示的最大整数为 \(xy-x-y\)
    • 又因为剩余系的性质,有当 \(a\) 跑遍模 \(y\) 的完全剩余系时 \(ax\) 也跑遍模 \(y\) 的完全剩余系。
    • 考虑从完全剩余系入手,枚举 \(a \in [0,y)\) 即可不重不漏地算完。
      • \(c=ax+by(b \le 0 \le a)\) ,移项有 \(ax-c=-by\) 。当 \(ax\) 确定时,因为 \(c \ge 1\)\(-b \in [1, \left\lfloor \dfrac{ax}{y} \right\rfloor]\)
    • 最终,有 \(\begin{aligned} \sum\limits_{i=0}^{y-1} \left\lfloor \frac{ix}{y} \right\rfloor &=\frac{\sum\limits_{i=0}^{y-1} (ix)-\sum\limits_{i=0}^{y-1} (ix \bmod y)}{y} \\ &=\frac{x \times \sum\limits_{i=0}^{y-1}i-\sum\limits_{i=0}^{y-1}i}{y} \\ &=\frac{\frac{xy(y-1)}{2}-\frac{y(y-1)}{2}}{y} \\ &=\frac{(x-1)(y-1)}{2} \end{aligned}\) 即为所求。
    点击查看代码
    ll gcd(ll a,ll b)
    {
    	return b?gcd(b,a%b):a;
    }
    int main()
    {
    	ll x,y;
    	cin>>x>>y;
    	cout<<(gcd(x,y)==1?(x-1)*(y-1)/2:-1)<<endl;
    	return 0;
    }
    

\(T4\) T2810. 春节十二响

  • 原题: luogu P5290 [十二省联考 2019] 春节十二响

  • 对于以 \(x\) 为根的子树内的节点分段时一定是和以 \(fa_{x}\) 为根的子树内除以 \(x\) 为根的子树外的其他子树的节点在同一个段。

  • 从贪心的角度分析,大的数和大的数在一起一定是最优的,优先队列或可并堆维护。

  • 启发式合并维护即可。

    点击查看代码
    struct node
    {
    	ll nxt,to;
    }e[200010];
    ll head[200010],a[200010],cnt=0;
    priority_queue<ll>q[200010],ls;
    void add(ll u,ll v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dsu_merge(ll x,ll y)
    {
    	if(q[x].size()<q[y].size())
    	{
    		swap(q[x],q[y]);
    	}
    	while(q[y].empty()==0)
    	{
    		ls.push(max(q[x].top(),q[y].top()));
    		q[x].pop();
    		q[y].pop();
    	}
    	while(ls.empty()==0)
    	{
    		q[x].push(ls.top());
    		ls.pop();
    	}
    }
    void dfs(ll x)
    {
    	for(ll i=head[x];i!=0;i=e[i].nxt)
    	{
    		dfs(e[i].to);
    		dsu_merge(x,e[i].to);
    	}
    	q[x].push(a[x]);
    }
    int main()
    {
    	ll n,u,v,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=2;i<=n;i++)
    	{
    		cin>>u;
    		v=i;
    		add(u,v);
    	}
    	dfs(1);
    	while(q[1].empty()==0)
    	{
    		ans+=q[1].top();
    		q[1].pop();
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

总结

  • \(T1\) 多次读假题。
  • \(T3\) 记错原题条件了,导致没想到会有无解的情况。

后记

  • \(T1\)\(tgOJ\) 上精心下发了数据生成器和少量随机数据,但随机数据中相邻两数间含有大量空格,疑似专门卡快读。
  • \(T2\) @Delov 学长称“这 \(DP\) 已经简单到不能在简单了。”
  • \(T4\) @Delov 学长称“没有发挥真正 \(T4\) 的作用”。
  • 因赛时没有提交代码被学长 \(D\) 了。
posted @ 2024-07-18 18:44  hzoi_Shadow  阅读(52)  评论(0编辑  收藏  举报
扩大
缩小