zoukankan      html  css  js  c++  java
  • 【解题报告】CSPS2020

    【解题报告】CSP-S2020

    从这一年开始NOIP和CSP正式变为四道题目,两天的考试已经成为历史了

    T1 儒略日

    思路

    当年这道题目把我恶心死了,导致我没办法去NOIP2020

    这道题目按理来说就是模拟,中间要处理一下格里高利历的空出的日期,以及公元前和公元后的一些闰年的处理,还有一些月份的处理

    时至今日,我还是不想打模拟题

    于是暗示了我的完美结局

    实际上我们可以预处理四百年的信息,再来根据这个信息稍微改变以及推算一下就好了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #define int long long
    using namespace std;
    const int maxn=146097;
    int q;
    int y[maxn],m[maxn],d[maxn];
    int r,tmp;
    int calc(int year,int month)
    {
    	if(month==2)
    	{
    		if(year%4!=0) return 28;
    		else
    		{
    			if(year%100!=0) return 29;
    			else
    			{
    				if(year%400!=0) return 28;
    				else return 29;
    			}
    		}
    	}
    	if(month==4||month==6||month==9||month==11)
    	return 30;
    	else return 31;
    }
    signed main()
    {
    	m[0]=d[0]=1;
    	for(int i=1;i<maxn;i++)
    	{
    		d[i]=d[i-1]+1;
    		m[i]=m[i-1];
    		y[i]=y[i-1];
    		if(d[i]>calc(y[i],m[i]))
    		{
    			m[i]++,d[i]=1;
    		}
    		if(m[i]>12)
    		{
    			y[i]++;
    			m[i]=1;
    		}
    	}
    	cin>>q;
    	while(q--)
    	{
    		cin>>r;
    		if(r>2299160)
    		{
    			r-=2159351;
    			tmp=r/maxn*400+1200;
    			r%=maxn;
    		}
    		else
    		{
    			tmp=r/1461*4-4712;
    			r%=1461;
    		}
    		cout<<d[r]<<" "<<m[r]<<" ";
    		if(tmp+y[r]>0)
    		cout<<tmp+y[r];
    		else
    		cout<<(1-tmp-y[r])<<" BC";
    		cout<<'\n';
    	}
    	return 0;
    }
    

    T2 动物园

    思路

    位运算

    甚至比T1还要简单

    我们直接用一个 unsigned long long 来储存所有的饲料要求

    然后对于所有的剩下的没有选的动物的编号跟要求进行对比

    然后我们对于每个要求,进行一次操作

    我们继续观察发现,既然每个动物都有的话,我们实际上要知道的就是饲料清单上第 \(p_i\) 位上为 \(0\)​ 的数目

    实际上答案就是 \(2^{k-S}-n\)

    注意 \(2^{64}\) 需要特判一下

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #define int unsigned long long
    using namespace std;
    int n,m,c,k;
    int ans,dw,ls;
    signed main()
    {
    	cin>>n>>m>>c>>k;
    	for(int i=1;i<=n;i++)
    	{
    		int x;
    		cin>>x;
    		dw|=x;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		int p,q;
    		cin>>p>>q;
    		ls|=1ull<<p;
    	}
    	for(int i=0;i<k;i++)
    	if(!((ls>>i)&1)||((dw>>i)&1))
    	ans++;
    	
    	if(ans==64&&n==0)
    	cout<<"18446744073709551616"<<'\n';//特判 
    	else
    	{
    		if(ans==64)
    		cout<<-n<<'\n';
    		else
    		cout<<(1ull<<ans)-n<<'\n';
    	}
    	return 0;
    }
    

    T3 函数调用

    思路

    当时考场上以为很简单,打了这个,结果空间爆了

    所以我们进行一波看

    我们可以先处理乘法,然后对于每次加法再弄一个乘法操作,相当于打一个懒惰标记

    然后我们再次了解发现,函数的调用关系实际上就是一个拓扑序

    我们做两次拓扑排序,一次处理函数执行一次后乘的倍数

    另一次处理函数等价于执行多少次

    因为乘法执行一次,等于加法执行若干次

    这样就做出来了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <queue>
    using namespace std;
    const int maxn=100005;
    const int mod=998244353;
    int n,m,q;
    int cnt[maxn];
    int a[maxn];
    int type[maxn],mul[maxn],add[maxn],pos[maxn];
    int out1[maxn],out2[maxn];
    vector<int> g1[maxn],g2[maxn];
    void topo1()
    {
    	queue <int> q;
    	for(int i=0;i<=m;i++)
    	{
    		out1[i]=g2[i].size();
    		if(out1[i]==0)
    		q.push(i);
    	}
    	while(q.size())
    	{
    		int x=q.front();
    		q.pop();
    		for(int i=0;i<g1[x].size();i++)
    		{
    			int e=g1[x][i];
    			mul[e]=1ll*mul[e]*mul[x]%mod;
    			out1[e]--;
    			if(out1[e]==0)q.push(e);
    		}
    	}
    }
    void topo2()
    {
    	queue <int> q;
    	for(int i=0;i<=m;i++)
    	{
    		out2[i]=g1[i].size();
    		if(out2[i]==0)
    		q.push(i);
    	}
    	while(q.size())
    	{
    		int x=q.front();
    		int tag=1;
    		q.pop();
    		for(int i=g2[x].size();i>0;--i)
    		{
    			int e=g2[x][i-1];
    			cnt[e]=(cnt[e]+1ll*cnt[x]*tag)%mod;
    			tag=1ll*tag*mul[e]%mod;
    			out2[e]--;
    			if(out2[e]==0)
    			q.push(e);
    		}
    	}
    }
    int main()
    {
    	std::ios::sync_with_stdio(false);
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	cin>>a[i];
    	cin>>m;
    	mul[0]=1;
    	for(int i=1;i<=m;i++)
    	{
    		cin>>type[i];
    		if(type[i]==1)
    		{
    			mul[i]=1;
    			cin>>pos[i]>>add[i];
    		}
    		if(type[i]==2)
    		cin>>mul[i];
    		if(type[i]==3)
    		{
    			mul[i]=1;
    			int c;
    			cin>>c;
    			for(int j=0;j<c;++j)
    			{
    				int e;
    				cin>>e;
    				g1[e].push_back(i);
    				g2[i].push_back(e);
    			}
    		}
    	}
    	cin>>q;
    	cnt[0]=1;
    	while(q--)
    	{
    		int x;
    		cin>>x;
    		g1[x].push_back(0);
    		g2[0].push_back(x);
    	}
    	topo1();
    	topo2();
    	for(int i=1;i<=n;i++)
    	a[i]=1ll*a[i]*mul[0]%mod;
    	for(int i=1;i<=m;i++)
    	{
    		if(type[i]==1)
    		a[pos[i]]=(a[pos[i]]+1ll*cnt[i]*add[i])%mod;
    	}
    	for(int i=1;i<=n;i++)
    	cout<<a[i]<<" ";
    	return 0;
    }
    

    T4 贪吃蛇

    思路

    贪心

    有一个结论,如果当前最强的蛇吃掉的最蒻的蛇之后,没有变成最蒻的蛇,那么他一定会继续吃

    现在考虑最强的蛇,如果吃掉一条蛇之后,他还是最强的蛇的话,肯定会吃

    如果吃掉一条蛇之后最强的蛇不是他了,这个时候,新的最强的蛇没有刚才强,现在最蒻的蛇也没有刚才蒻,第二强的蛇吃掉了最蒻的蛇之后一定没有第一次最强的更强,所以会死在第一次最强的前面,所以如果这个蛇能想办法不死的话,那么第一次最强的蛇也一定不会死

    如果吃完蛇之后会变成最蒻的蛇,吃不吃呢?

    如果吃的话,我们第二次最强的蛇如果吃掉了第一次最强的蛇

    如果不是最蒻的蛇的话,那么第二次最强的蛇一定会吃掉第一次最强的蛇,那么第一次最强的蛇则不会选择吃最蒻的蛇

    同理,如果第二次最强的蛇吃掉最蒻的蛇之后变成了最蒻的蛇,我们按照刚才相同的思路考虑第三条蛇吃不吃,依次考虑下去,子子孙孙无穷匮也

    所以我们只用模拟一下两个阶段

    第一个阶段是,所有最强的蛇进食之后肯定不是最强的蛇,所以我们直接吃最蒻的蛇

    第二个阶段是,所有最强的蛇进食之后都是最蒻的蛇,直到有一条蛇可以放心大胆吃

    阶段一结束的时候,游戏就基本完了

    我们可以用双端队列来维护蛇

    因为用双端队列是有序进来的,所以具有单调性

    对于第一个阶段和第二个阶段,我们这么模拟

    第一个阶段:

    我们每次从 \(q_1,q_2\) 的尾部取出最强的蛇,从 \(q_1\) 的头部取出最蒻的蛇,如果吃完了之后是最蒻的,那就进入第二阶段,否则装进 \(q_2\) 的头部,继续上述过程

    第二个阶段:

    此时最蒻的蛇没必要在入队了,我们一直进食,知道总共的蛇的数量等于 \(2\) 或者进食之后不是最蒻的为止,我们找最强的蛇依然是从 \(q_1,q_2\) 的队尾找

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    const int maxn=1000005;
    const int inf=1e9;
    int T,n,a[maxn];
    pair <int,int> q1[maxn],q2[maxn];
    int l1,r1,l2,r2;
    pair <int,int> get_max()//取最大蛇 
    {
    	if(r1==l1)
    	return q2[l2++];
    	else if(r2==l2)
    	return q1[--r1];
    	else if(q2[l2]>q1[r1-1])
    	return q2[l2++];
    	else return q1[--r1];
    }
    pair <int,int> get_min()//取最小 
    {
    	if(l1==r1)
    	return q2[--r2];
    	else if(r2==l2)
    	return q1[l1++];
    	else if(q2[r2-1]<q1[l1])
    	return q2[--r2];
    	else return q1[l1++];
    }
    pair <int,int> Min(pair <int,int> x,pair <int,int> y)
    {
    	return x<y?x:y;
    }
    inline void solve()
    {
    	l1=r1=l2=r2=0;
    	for(int i=1;i<=n;++i)
    	q1[r1++]=make_pair(a[i],i);
    	int flag=0,cnt=0,alf=0;
    	while(1)
    	{
    		cnt++;
    		pair <int,int> x=get_min(),y=get_max();
    		pair <int,int> z=Min((l1<r1?q1[l1]:make_pair(inf,-inf)),(l2<r2?q2[r2-1]:make_pair(inf,-inf)));
    		y.first-=x.first;
    		if(y>z||cnt==n-1)
    		{
    			if(flag)
    			{
    				cout<<n-(flag-(alf&1))<<'\n';
    				return ;
    			}
    			if(cnt==n-1)
    			{
    				cout<<1<<'\n';
    				return ;
    			}
    			q2[r2++]=y;
    		}
    		else 
    		{
    			alf++;
    			if(!flag)
    			flag=cnt;
    			q2[r2++]=y;
    		}
    	}
    }
    int main()
    {
    	cin>>T;
    	T--;
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	cin>>a[i];
    	solve();
    	while(T--)
    	{
    		int k;
    		cin>>k;
    		for(int i=1;i<=k;i++)
    		{
    			int x;
    			cin>>x;
    			cin>>a[x];
    		}
    		solve();
    	}
    	return 0;
    }
    
    本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
  • 相关阅读:
    Nginx介绍
    linux vi编辑
    MySql数据类型
    Mysql用户权限控制(5.7以上版本)
    Linux上安装MySQL
    Java得到指定日期的时间
    Spring Boot 整合Redis 实现缓存
    编写高效优雅Java程序
    JVM调优和深入了解性能优化
    JVM执行子程序
  • 原文地址:https://www.cnblogs.com/wweiyi2004/p/15572664.html
Copyright © 2011-2022 走看看