CSP-S2 2020 Solution

CSP-S2 2020 Solution

冷静思考,没有什么能拦住你!!!

T1----julian

Review

直接考察了程序组织实现能力。清晰的模块化构思会帮助实现。

强大的心理以及冷静分析相当重要。

关于实现的几点细节:

  1. 1582年10月15日前后适用的历法不同—主要不同在于闰年的判定。
  2. 有一段时间的删除:1582年10月5日 至 1582年10月14日。
  3. 公元0年不存在,即公元前1年后一年为公元1年。
  4. 公元前1年、公元前3年、公元前5年……为闰年。
  5. 输入数据需用long long 存储。。。

有不少人通过重重 if 判断处理这些细节,十分繁琐(听说有 5k + 2.5 h 的)
其实如果先想好,细节分几点、如何处理,就不用那么复杂了。

我的实现方式:

  1. 对于1、2两点细节,可以通过暴力找到分界点对应的儒略日,循环中不必再讨论过多。
  2. 对于3、4,则可以通过把公元前4713年设为公元-4712年解决,输出时简单判断就好。
  3. 发现历法有400年的周期,也可暴力出来。一年年跳后直接把一天天跳的暴力copy上就好。

Code

#include<cstdio>
#define ll long long 
using namespace std;

const int d[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int Q;
ll r;int Y,M,D;
ll k;
int yd,cy;
int main()
{
	freopen("julian.in","r",stdin);
	freopen("julian.out","w",stdout);
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%lld",&r);
		Y=-4712;
		M=1;
		D=1;
		if (r<=2299160)
		{
			k=r/146100;
			r%=146100;
			Y+=k*400;
			while (r)
			{
				if (M==1||(M==2&&D<29))cy=Y;else cy=Y+1;
				if (cy%4==0)yd=366;else yd=365;
				if (r>=yd)
				{
					r-=yd;
					++Y;
				}else break;
			}
			if (r)
			{
				while (r)
				{
					if (M==2)
					{
						if (Y%4==0)
						{
							if (D==29)
							{
								++M;
								D=1;
							}else ++D;
						}
						else
						{
							if (D==28)
							{
								++M;
								D=1;
							}else ++D;
						}
					}else
					{
						if (D==d[M])
						{
							if (++M==13)M=1,++Y;
							D=1;
						}else ++D;
					}
					--r;
				}
			}
			if (Y>0)printf("%d %d %d\n",D,M,Y);
			else
			{
				printf("%d %d %d BC\n",D,M,-Y+1);
			}
		}else
		{
			r-=2299161;
			Y=1582;
			M=10;
			D=15;
			
			k=r/146097;
			r%=146097;
			Y+=k*400;
			while (r)
			{
				if (M==1||(M==2&&D<29))cy=Y;else cy=Y+1;
				if ((cy%400==0||(cy%4==0&&cy%100!=0)))yd=366;else yd=365;
				if (r>=yd)
				{
					r-=yd;
					++Y;
				}else break;
			}
			if (r)
			{
				while (r)
				{
					if (M==2)
					{
						if (Y%400==0||(Y%4==0&&Y%100!=0))
						{
							if (D==29)
							{
								++M;
								D=1;
							}else ++D;
						}
						else
						{
							if (D==28)
							{
								++M;
								D=1;
							}else ++D;
						}
					}else
					{
						if (D==d[M])
						{
							if (++M==13)M=1,++Y;
							D=1;
						}else ++D;
					}
					--r;
				}
			}
			printf("%d %d %d\n",D,M,Y);
		}
	}
	return 0;
}

T2----Zoo

Review

考察阅读能力

考察语言掌握???好吧,还是有一点点技巧的吧。

几点细节:

  1. k可能为64,这使得不少变量需要用unsigned long long 存储。
  2. n可能为0,因此答案有可能会爆 ull —特判。

实现方式:

  1. a i a_i ai可以或起来后再算哪些位有1
  2. 老实打哈希—打了离散化的我T飞了(坏习惯)
  3. 变量类型

Code

#include<cstdio>
#include<algorithm>
#define N 1000005
#define ll unsigned long long
using namespace std;
int read()
{
	int x=0;char ch=getchar();
	while (ch<'0'||ch>'9')ch=getchar();
	while (ch>='0'&&ch<='9')
	{
		x=x*10+(ch^48);
		ch=getchar();
	}
	return x;
}
ll lread()
{
	ll x=0;char ch=getchar();
	while (ch<'0'||ch>'9')ch=getchar();
	while (ch>='0'&&ch<='9')
	{
		x=x*10+(ch^48);
		ch=getchar();
	}
	return x;
}
ll w[70];
int n,m,c,k;
bool bz[70];
int lk[70];
struct edge{
	int to,nx;
}e[N];int tot;

inline void add(int u,int v)
{
	e[++tot].to=v;e[tot].nx=lk[u];lk[u]=tot;
}

bool ab[70];
const int mo=19491001;
struct hash{
	int h[mo];
	inline int get(int x)
	{
		int p=x%mo;
		while (h[p]&&h[p]!=x)
		if (++p==mo)p=0;
		return p;
	}
	inline void add(int x){h[get(x)]=x;}
	inline bool in(int x){return h[get(x)];}
}H;
int main()
{
	freopen("zoo.in","r",stdin);
	freopen("zoo.out","w",stdout);
	n=read();m=read();
	c=read();k=read();
	w[0]=1;
	for (int i=1;i<k;++i)w[i]=w[i-1]<<1;
	ll A=0;
	for (int i=1;i<=n;++i)A|=lread();
	for (int i=0;i<k;++i)
	if (w[i]&A)bz[i]=1;
	for (int i=1,p,q;i<=m;++i)
	{
		p=read();
		q=read();
		if (p<k)
		{
			add(p,q);
			if (bz[p])
			H.add(q);
		}
	}
	for (int i=0;i<k;++i)
	if (bz[i])ab[i]=1;
	else
	{
		bool able=1;
		for (int j=lk[i];j;j=e[j].nx)
		if (!H.in(e[j].to))
		{
			able=0;
			break;
		}
		if (able)ab[i]=1;
	}
	int sum=0;
	for (int i=0;i<k;++i)sum+=ab[i];
	if (sum==k)
	{
		if (k==64&&n==0)puts("18446744073709551616");
		else printf("%llu\n",(w[k-1]-n)+w[k-1]);
	}else
	printf("%llu\n",w[sum]-n);
	return 0;
}

T3----Call

Review

简单模型转化&运算简化

细节!?:

  1. 2操作可能会乘以0—听说有用的逆元做法?
  2. 一个函数的层层调用情况构成一个DAG

进行转化:

  • 模型:图。
  • 运算:
    、、发现乘法只对全局操作,所有乘操作统一处理。加操作会被后面的乘操作影响,考虑乘操作影响如何快速统计。
    、、可以发现,如果对于每次“函数调用”,都往下进行一次处理,那么时间复杂度无法保证。此时容易注意到,将不同时间调用的同类函数同时处理看上去可行(它们的DAG同构),而且复杂度似乎可行( ∑ c i ≤ 1 0 6 \sum c_i \leq 10^6 ci106)。
    、、怎么做?----分开考虑 1)函数调用的函数间的影响(乘操作,后同)以及 2)该(种)函数调用之后出现的函数对当前函数的影响。
    、、把多次同种函数一起做?-> 将 2)影响的计算通过乘法分配律合并(往函数分支中计算时的转移都是乘以相同的数)。。。。一遍拓扑排序就没了?!是的。

难点就是一点转化,这就把不少人吓跑了。。

Code

#include<cstdio>
#define N 1000005
#define M 1000005
#define ll long long
#define mo 998244353
#define plus(x,y) if ((x+=(y))>=mo)x-=mo
using namespace std;
int read()
{
	int x=0;char ch=getchar();
	while (ch<'0'||ch>'9')ch=getchar();
	while (ch>='0'&&ch<='9')
	{
		x=x*10+(ch^48);
		ch=getchar();
	}
	return x;
}
bool sp[N],hf[N];
int n,a[N];
int m;
struct edge{
	int to,nx;
	ll w;
}e[M*6];int tot;
int d[N];
struct pro{
	ll mul,Mul,Smul;
	int lk,typ;
	inline void add(int to,int w)
	{
		e[++tot].to=to;
		if (typ==3)++d[to];
		e[tot].nx=lk;
		e[tot].w=w;
		lk=tot;
	}
}p[N];
int temp[M];
int f[N],Q;
void getMul(int x)
{
	if (p[x].Mul||sp[x])return;
	if (p[x].typ==1)
	{
		p[x].Mul=1;
		return;
	}else if (p[x].typ==2)
	{
		p[x].Mul=p[x].mul;
		return;
	}
	p[x].Mul=1;
	for (int i=p[x].lk;i;i=e[i].nx)
	{
		getMul(e[i].to);
		if (sp[e[i].to])
		{
			sp[x]=1;
			p[x].Mul=0;
			return;
		}
		p[x].Mul=p[x].Mul*p[e[i].to].Mul%mo;
	}
}
ll Smul=1;
ll b[N];
void solve(int x)
{
	--d[x];
	if (p[x].Smul)
	{
		if (p[x].typ==1)
		{
			plus(b[e[p[x].lk].to],(ll)e[p[x].lk].w*p[x].Smul%mo);
		}else if (p[x].typ==2)
		{
			return;
		}else
		{
			ll Mul=p[x].Smul;
			for (int i=p[x].lk;i;i=e[i].nx)
			{
				plus(p[e[i].to].Smul,Mul%mo);
				Mul=Mul*p[e[i].to].Mul%mo;
				if (!--d[e[i].to])
				solve(e[i].to);
			}
		}
	}else if (p[x].typ==3)
	{
		for (int i=p[x].lk;i;i=e[i].nx)
		if (!--d[e[i].to])solve(e[i].to);
	}
}
int main()
{
	freopen("call.in","r",stdin);
	freopen("call.out","w",stdout);
	n=read();
	for (int i=1;i<=n;++i)a[i]=read();
	m=read();
	for (int i=1,x,y;i<=m;++i)
	{
		p[i].typ=read();
		p[i].Smul=0;
		if (p[i].typ==1)
		{
			p[i].mul=1;
			x=read();
			y=read();
			p[i].lk=0;
			p[i].add(x,y);
		}else if (p[i].typ==2)
		{
			p[i].mul=read();
			if (!p[i].mul)sp[i]=1;
			p[i].lk=0;
		}else
		{
			x=read();
			p[i].lk=0;
			for (int j=1;j<=x;++j)
			p[i].add(read(),-1);
		}
	}
	for (int i=1;i<=m;++i)getMul(i);
	Q=read();
	for (int i=1;i<=Q;++i)
	{
		f[i]=read();
		Smul=Smul*p[f[i]].Mul%mo;
	}
	ll Mul=1;
	for (int i=Q;i;--i)
	{
		plus(p[f[i]].Smul,Mul);
		Mul=Mul*p[f[i]].Mul%mo;
	}
	for (int i=1;i<=m;++i)
	if (!d[i])solve(i);
	for (int i=1;i<=n;++i)
	{
		printf("%lld ",(Smul*a[i]+b[i])%mo);
	}
	return 0;
}

T4----Snakes

Review

小博弈题+实现技巧。

提供几条思考方向:

  1. 由于胜负与相对大小相联系,考虑模拟几轮游戏,看看与大小变化有关的规律----我建议不要总是带入(拿脚造的)样例数据思考,从宏观的游戏局面思考总是更加严谨全面
  2. 直接从获胜(或取得更优状态)的充要条件出发推理各个情况。

以下“我”指编号为n的蛇
来推一波策略把:
1.
、、发现我吃了第一条蛇后的情况显然可以分为两种:1)我成了最弱的;2)我不是最弱的。(划分依据:直接与下一轮游戏的参与状况相联系)。
、、1)发现转化为判断下一位对手是否会吃我。而 2)情况似乎很复杂。不过我们可以继续1的思考方向—再模拟一轮。可以马上发现此轮吃蛇的蛇会比我更弱–>它会比我更早死;而它至少能靠停止吃蛇生存,那么我就不怕能不能吃蛇了。即遇到这种情况就一定会吃。
2.
、、仍然分出上面两种情况 1)2)。
、、1)若当前我吃了更优,那么下一轮我不能被吃,推理结果同上。
、、2)若当前我吃了更优,那么以后我不能被吃……推理结果同上。

两种情况的简化的博弈策略相结合,可以容易得到进一步的做法。

点一点实现技巧:与「noip2016 蚯蚓」做法类似。
由于每一轮游戏出现的新蛇体力单调递减,我们每次又只用找出体力最小/大的几条蛇,
新开一个队列就能完成有序队列的维护。

Code

#include<cstdio>
#include<algorithm>
#define N 1000005
using namespace std;
int read()
{
	int x=0;char ch=getchar();
	while (ch<'0'||ch>'9')ch=getchar();
	while (ch>='0'&&ch<='9')
	{
		x=x*10+(ch^48);
		ch=getchar();
	}
	return x;
}
typedef pair<int,int> P;
int n,T;
P a[N],b[N];
P operator -(P A,P B){return P(A.first-B.first,A.second);}
inline void solve()
{
	int h1=1,t1=n,h2=1,t2=0,ans=n;
	P mi,se,mx;
	while (1)
	{
		if (t1-h1+t2-h2==0)
		{
			printf("%d\n",ans-1);
			return;
		}
		mi=a[t1--];
		if (h1<=t1&&a[t1]<b[t2]||h2>t2)se=a[t1];
		else se=b[t2];
		if (h1<=t1&&a[h1]>b[h2]||h2>t2)
		{
			if (se<a[h1]-mi)
			{
				++t2;
				b[t2]=P(a[h1].first-mi.first,a[h1].second);
				--ans;
				++h1;
			}else
			{
				P A=a[h1++]-mi;
				bool sig=0;
				while (A<se)
				{
					if (t1-h1+t2-h2+2==1)
					{
						sig^=1;
						break;
					}
					if (h1<=t1&&a[h1]>b[h2]||h2>t2)A=a[h1++]-A;
					else A=b[h2++]-A;
					sig^=1;
				}
				if (!sig)--ans;
				printf("%d\n",ans);
				return;
			}
		}
		else// 这部分好像不用,但我懒得改
		{
			if (se<b[h2]-mi)
			{
				++t2;
				b[t2]=P(b[h2].first-mi.first,b[h2].second);
				--ans;
				++h2;
			}else
			{
				P A=b[h2++]-mi;
				bool sig=0;
				while (A<se)
				{
					if (t1-h1+t2-h2+2==1)
					{
						sig^=1;
						break;
					}
					if (h1<=t1&&a[h1]>b[h2]||h2>t2)A=a[h1++]-A;
					else A=b[h2++]-A;
					sig^=1;
				}
				if (!sig)--ans;
				printf("%d\n",ans);
				return;
			}
		}
	}
}
int main()
{
	freopen("snakes.in","r",stdin);
	freopen("snakes.out","w",stdout);
	T=read()-1;
	n=read();
	for (int i=1;i<=n;++i)a[n-i+1]=P(read(),i);
	solve();
	int k,x;
	while (T--)
	{
		k=read();
		while (k--)
		{
			x=read();
			a[n-x+1].first=read();
		}
		solve();
	}
	return 0;
}
2++]-mi;
				bool sig=0;
				while (A<se)
				{
					if (t1-h1+t2-h2+2==1)
					{
						sig^=1;
						break;
					}
					if (h1<=t1&&a[h1]>b[h2]||h2>t2)A=a[h1++]-A;
					else A=b[h2++]-A;
					sig^=1;
				}
				if (!sig)--ans;
				printf("%d\n",ans);
				return;
			}
		}
	}
}
int main()
{
	freopen("snakes.in","r",stdin);
	freopen("snakes.out","w",stdout);
	T=read()-1;
	n=read();
	for (int i=1;i<=n;++i)a[n-i+1]=P(read(),i);
	solve();
	int k,x;
	while (T--)
	{
		k=read();
		while (k--)
		{
			x=read();
			a[n-x+1].first=read();
		}
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值