[整理]CSP-S2020第二轮试题解析

题面请自行右转洛谷或任意一个能看到题面的地方。

T1 Julian

Solution:暴力模拟即可,但是需要进行一些运算来减少时间,同时请注意特判。
Code:考场代码(好长啊)
P.S.这个代码在洛谷上(官方数据)可以得到100分,但是€€£的菜机测出来好像只有六七十。

T2 Zoo

考场上思路想了一半自己给假了结果最后只打了个40分暴力qaq
Solution:可以先把动物和饲料预处理一下,将读入的动物或起来,然后再看哪些位需要饲料((q_i)互不相同所以没用了)。
然后按位考虑什么情况下饲料可以任取:要么是这一位上没有做出要求(所有动物都不需要这种饲料),要么是这一位已经有了动物。
所以把饲料取反,再和动物或起来,就得到了可以任取的位数。(此处可以参照代码理解)
但是不幸的是这道题连ull都卡了,需要特判一个(k=64)的情况。
Code:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define us unsigned
#define LL long long
#define rg register
using namespace std;
inline void Read(int &x){
	int f=1;
	x=0;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=(x<<1)+(x<<3)+c-48;
		c=getchar();
	}
	x*=f;
}
#define N 1000010
int n,m,c,k,p,q;
us LL a,animal,food,ans=1;
int main(){
	Read(n),Read(m),Read(c),Read(k);
	for(rg int i=1;i<=n;i++)scanf("%llu",&a),animal|=a;
	for(rg int i=1;i<=m;i++)Read(p),Read(q),food|=1ull<<p;
	animal|=~food;
	for(rg int i=0;i<k;i++){
		ans<<=animal>>i&1;
	}
	if(!ans&&!n)cout<<"18446744073709551616"<<endl;
	else printf("%llu
",ans-n);
	return 0;
}

T3 Call

Solution:我们考虑操作之间会出现什么影响。
不难发现每个加操作所乘的系数就是它后面的所有乘法操作的后缀积。所以我们可以对每个函数记一个mul表示它所产生的乘法影响(1操作的mul就是1)。
此时一个3操作的mul就是它所属儿子的mul之积。
此时设sum为当前3操作带的系数,在只有加的情况下我们可以直接下传,但是如果加乘都有,我们就需要做一点改变:
我们在下传sum的时候,需要对每一个加操作乘上一个当前3操作内部的后缀积。
pic
如图,+2操作要带上*12的系数,+3操作要带上*4的系数。
Implementation:我们先把函数按照拓扑序排序,然后用两个函数分别实现求mul和求sum
Code:

const int N=1000010;
const int mod=998244353;
int n,m,a[N],q,f[N];
int ind[N],tp[N],tot;
struct Function {
	int opt,pos,num,mul,sum;
}c[N];
struct Edge {
	int to,nxt;
}e[N<<1];
int head[N],cnt;
inline void ade(int u,int v){
	e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
}
void Topo(){
	queue<int>q;
	for(rg int i=1;i<=m;i++)if(!ind[i])q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		tp[++tot]=u;
		for(rg int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(!--ind[v])q.push(v);
		}
	}
}
inline void GetProd(){
	for(rg int k=m;k;k--){
		int u=tp[k];
		for(rg int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			c[u].mul=c[u].mul*c[v].mul%mod;
		}
	}
}
inline void GetSum(){
	for(rg int k=1;k<=m;k++){
		int u=tp[k],suf=1;
		for(rg int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			c[v].sum=(c[v].sum+c[u].sum*suf%mod)%mod;
			suf=suf*c[v].mul%mod;
		}
	}
}
signed main(){
	Read(n);
	for(rg int i=1;i<=n;i++)Read(a[i]);
	Read(m);
	for(rg int i=1;i<=m;i++){
		Read(c[i].opt);
		if(c[i].opt==1){
			Read(c[i].pos),Read(c[i].num),c[i].mul=1;
		}else if(c[i].opt==2){
			Read(c[i].num),c[i].mul=c[i].num;
		}else {
			int cj;Read(cj),c[i].mul=1;
			for(rg int j=1,g;j<=cj;j++){
				Read(g),ade(i,g),ind[g]++;
			}
		}
	}
	Topo();GetProd();
	Read(q);
	int suf=1;//suffix product
	for(rg int i=1;i<=q;i++)Read(f[i]);
	for(rg int i=q;i;i--){
		c[f[i]].sum=(c[f[i]].sum+suf)%mod;
		suf=suf*c[f[i]].mul%mod;
	}
	GetSum();
	for(rg int i=1;i<=n;i++)a[i]=a[i]*suf%mod;
	for(rg int i=1;i<=m;i++){
		if(c[i].opt==1){
			a[c[i].pos]=(a[c[i].pos]+c[i].num*c[i].sum%mod)%mod;
		}
	}
	for(rg int i=1;i<=n;i++)cout<<a[i]<<" ";
	return 0;
}

T4 Snakes

Solution:我们简单分析一下即可发现本题其实就是决策最强蛇吃不吃的问题,分三种情况:
第一种情况,最强蛇吃了之后还是最强的,那么它没有危险,不吃白不吃;
第二种情况,最强蛇吃了之后不是最强也不是最弱,那么接下来的蛇吃了之后肯定比它还弱,也没有危险,放心吃;
第三种情况,最强蛇吃了之后变为最弱蛇,此时比较复杂,我们讨论一下。
假设蛇A吃了之后变为最弱蛇,此时的最强蛇蛇B如果属于前两种情况,蛇A就没了,此时一定选择不吃;
如果此时最强蛇蛇B吃了后也是最弱的,那么蛇B就要看下一条最强蛇蛇C的情况,以此类推。
这样一直考虑下去,直到某条蛇吃了之后不是最弱蛇或者剩下两条蛇(设为蛇X),那么蛇X会选择吃,蛇X-1会选择不吃,蛇X-2会吃……
也就是说,我们需要考虑最后那条蛇的奇偶性(例如:最强蛇是蛇X-1就直接停止,最强蛇是蛇X-2就吃一轮然后在蛇X-1处停止)。
整理一下,我们的决斗可以分为两个阶段:
第一阶段,所有最强蛇随便吃;
第二阶段,最强蛇吃了后变成最弱蛇,需要选择吃还是不吃,此时的决斗最多再进行一轮。
Implementation:可以用set来实现取最强最弱,但是由于复杂度(O(log n))只能得70分。
正解:使用两个双端队列优化为(O(1))修改。开两个队列(q1,q2)(q1)先赋值为原数组,然后每次取(q1,q2)的队尾最大值减去(q1)队头最大值放入(q2)队头,可以证明此时(q2)也满足单调性。
Code:
不放了反正也是模仿的洛谷题解

原文地址:https://www.cnblogs.com/juruoajh/p/14007943.html