CSP-S2023题解

考场上只做出来两题,赛后发现好像都不难

T1:

shaber题,爆搜就行了

考场代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[15][15];
int b[15];
int ans;
bool check()
{
	for(int i=1;i<=n;i++)
	{
		int las=0;
		for(int j=1;j<=5;j++)
		{
			if(b[j]!=a[i][j])
			{
				if(las==-1)return false;
				if(las)
				{
					int p1=0,p2=0,q1=0,q2=0;
					if(las!=j-1)return false;
					if(b[j]<a[i][j])p1=b[j]+10-a[i][j],p2=a[i][j]-b[j];
					else p1=b[j]-a[i][j],p2=a[i][j]+10-b[j];
					if(b[j-1]<a[i][las])q1=b[j-1]+10-a[i][las],q2=a[i][las]-b[j-1];
					else q1=b[j-1]-a[i][las],q2=a[i][las]+10-b[j-1];
					las=-1;
					if(p1!=q1 && p2!=q2)return false;
				}
				else las=j;
			}
		}
		if(las==0)return false;
	}
	return true;
}
void dfs(int i)
{
	if(i==6)
	{
		if(check())ans++;
		return ;
	}
	for(int j=1;j<=9;j++)
	{
		b[i]=j;
		dfs(i+1);
	}
	b[i]=0;
	dfs(i+1);
}
int main()
{
//	freopen("lock.in","r",stdin);
//	freopen("lock.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=5;j++)cin>>a[i][j];
	}
	dfs(1);
	cout<<ans;
	return 0;
} 

T2:

很容易想到一个\(n^2\) 的做法,钦定$i $为左端点,枚举右端点,用一个栈判断区间是否合法。

考虑优化这个做法,首先注意到我们只需要找到以\(i\) 为左端点最小的合法区间就行,用\(f[i]\) 表示以\(i\) 为左端点,有多少个合法区间,设\(j\) 为以\(i\) 为左端点,\([i,j]\) 是最小的合法区间,因为合法区间可以是由多个合法区间合起来的,所以以\(i\) 为左端点的合法区间肯定是由\([i,j],[j+1,?]\) 组合起来的,所以\(f[i]=f[j+1]+1\)

那么怎么找以\(i\) 为左端点最小的合法区间呢,显然如果这个区间是个最小的合法区间,那么\(s[j]\)肯定与\(s[i]\) 相等,且\([i+1,j-1]\) 为合法区间,考虑记录每个点以自己为左端点的最小合法区间右端点的位置,只需要从i+1一直跳,直到跳到一个字母与$s[i] $相等的就结束,这个位置就是最小的合法区间的右端点。

时间复杂度大概是\(26n\) 吧,不会证明,但是CCF的数据跑得挺快的,最大的点也只有\(80ms\)

考场代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int n;
long long f[maxn],ans;
int r[maxn];
string s;
int main()
{
//	freopen("game.in","r",stdin);
//	freopen("game.out","w",stdout);
	cin>>n;
	cin>>s;
	for(int i=n-1;i>=0;i--)
	{
		r[i]=n;
		int j=i+1;
		while(j<n)
		{
			if(s[j]==s[i])
			{
				r[i]=j+1;
				f[i]+=f[j+1]+1;
				break;
			}
			j=r[j];
		}
		ans+=f[i];
	}
	cout<<ans;
	return 0;
} 

T3:

这题考场上没做出来,看题目太长了就去做T2了,赛后2.5h打完了,还是很后悔的。

前期工作:

类型:对每个类型建一个结构体

struct LX
{
	int nc,dj;//大小,对齐要求
	int cys;//成员数
	st cy[105];//成员都是啥
	string na;//名字
	vector<int >s;//成员的起始位置
	map<string,int>ys;//某个字符串是第几个成员
}c[1005];

元素:对每个元素建一个结构体

struct st
{
	int l,r,lx;//在内存中的起始位置和结束位置,类型
	string na;//名字
}a[1000005];

操作一:

暴力更新一下结构体的各项数据,也不难理解


cin>>c[++cnt1].na>>c[cnt1].cys;
q[c[cnt1].na]=cnt1;
int las1=0;
for(int j=1;j<=c[cnt1].cys;j++)
{
	string lx,x;
	cin>>lx>>c[cnt1].cy[j].na;
	c[cnt1].cy[j].lx=q[lx];
	if(las1==0)c[cnt1].cy[j].l=0;
	else c[cnt1].cy[j].l=ce(las1,c[c[cnt1].cy[j].lx].dj);
	c[cnt1].ys[c[cnt1].cy[j].na]=j;
	c[cnt1].cy[j].r=c[cnt1].cy[j].l+c[c[cnt1].cy[j].lx].nc-1;
	c[cnt1].dj=max(c[cnt1].dj,c[c[cnt1].cy[j].lx].dj);
	las1=c[cnt1].cy[j].r+1;
	c[cnt1].s.push_back(j);
}
c[cnt1].nc=ce(c[cnt1].cy[c[cnt1].cys].r+1,c[cnt1].dj);
cout<<c[cnt1].nc<<" "<<c[cnt1].dj<<endl;

操作2:

和操作1差不多,把这个变量看做是0号类型的成员

string lx,x;
cin>>lx>>c[0].cy[++cnt].na;
c[0].cys++;
c[0].cy[cnt].lx=q[lx];
if(las==0)
{
	c[0].cy[cnt].l=0;
}
else c[0].cy[cnt].l=ce(las,c[c[0].cy[cnt].lx].dj);
c[0].ys[c[0].cy[cnt].na]=cnt;
c[0].cy[cnt].r=c[0].cy[cnt].l+c[c[0].cy[cnt].lx].nc-1;
c[0].dj=max(c[0].dj,c[c[0].cy[cnt].lx].dj);
las=c[0].cy[cnt].r+1;
c[0].s.push_back(cnt);
cout<<c[0].cy[cnt].l<<endl;

操作3:

用一个函数\(fi(i,s)\) 表示在i号结构体中,元素s的起始位置,一层一层的去找就行了。

int fi(int lx,string nam)
{
	int k=nam.find(".");
	if(k!=-1)
	{
		string nam1=nam.substr(0,k);
		nam.erase(0,k+1);
		return c[lx].cy[c[lx].ys[nam1]].l+fi(c[lx].cy[c[lx].ys[nam1]].lx,nam);
	}
	return c[lx].cy[c[lx].ys[nam]].l;
}

操作4:

用函数$fin(i,x) $表示在i号结构体中x内存对应的元素,和操作3差不多,一层一层的找,用二分找出第一个起始位置大于x位置的元素,然后看一下这个元素的结束位置是否小于x,小于x说明x位置是空的,返回ERR,不然就正常返回,如果这个元素没有成员了,说明他是基本类型,返回空。

这里要注意的是要特判一下0号结构体成员为空的情况

string fin(int lx,int x)
{
	int l=1,r=c[lx].cys;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(c[lx].cy[mid].l>x)r=mid-1;
		else l=mid+1;
	}
	if(c[lx].cys==0)
	{
		if(lx==0)return "ERR";
		return "";
	}
	string k=fin(c[lx].cy[r].lx,x-c[lx].cy[r].l);
	if(k=="")
	{
		if(c[lx].cy[r].r<x)
		{
			return "ERR";
		}
		return c[lx].cy[r].na;
	}
	if(k=="ERR")return "ERR";
	return c[lx].cy[r].na+"."+k;
}

最后再把这4种操作结合起来,我们就成功做出这道毒瘤大模拟了。

完整代码:

#include<bits/stdc++.h>
#define int long long
#define ce(a,b) (((a-1)/b+1)*b)
using namespace std;
int n,op;
int las=0;
vector<pair <int ,int > >f;
int cnt;
struct st
{
	int l,r,lx;
	string na;
}a[1000005];
int cnt1;
struct LX
{
	int nc,dj;
	int cys;
	st cy[105];
	string na;
	vector<int >s;
	map<string,int>ys;
}c[1005];
map<string,int>q;
int fi(int lx,string nam)
{
	int k=nam.find(".");
	if(k!=-1)
	{
		string nam1=nam.substr(0,k);
		nam.erase(0,k+1);
		return c[lx].cy[c[lx].ys[nam1]].l+fi(c[lx].cy[c[lx].ys[nam1]].lx,nam);
	}
	return c[lx].cy[c[lx].ys[nam]].l;
}
string fin(int lx,int x)
{
	int l=1,r=c[lx].cys;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(c[lx].cy[mid].l>x)r=mid-1;
		else l=mid+1;
	}
	if(c[lx].cys==0)
	{
		if(lx==0)return "ERR";
		return "";
	}
	string k=fin(c[lx].cy[r].lx,x-c[lx].cy[r].l);
	if(k=="")
	{
		if(c[lx].cy[r].r<x)
		{
			return "ERR";
		}
		return c[lx].cy[r].na;
	}
	if(k=="ERR")return "ERR";
	return c[lx].cy[r].na+"."+k;
}
signed main()
{
	cin>>n;
	q["byte"]=1;
	q["short"]=2;
	q["int"]=3;
	q["long"]=4;
	c[1].nc=1;
	c[2].nc=2;
	c[3].nc=4;
	c[4].nc=8;
	c[1].dj=1;
	c[2].dj=2;
	c[3].dj=4;
	c[4].dj=8;
	cnt1=4;
	for(int i=1;i<=n;i++)
	{
		cin>>op;
		if(op==1)
		{
			cin>>c[++cnt1].na>>c[cnt1].cys;
			q[c[cnt1].na]=cnt1;
			int las1=0;
			for(int j=1;j<=c[cnt1].cys;j++)
			{
				string lx,x;
				cin>>lx>>c[cnt1].cy[j].na;
				c[cnt1].cy[j].lx=q[lx];
				if(las1==0)c[cnt1].cy[j].l=0;
				else c[cnt1].cy[j].l=ce(las1,c[c[cnt1].cy[j].lx].dj);
				c[cnt1].ys[c[cnt1].cy[j].na]=j;
				c[cnt1].cy[j].r=c[cnt1].cy[j].l+c[c[cnt1].cy[j].lx].nc-1;
				c[cnt1].dj=max(c[cnt1].dj,c[c[cnt1].cy[j].lx].dj);
				las1=c[cnt1].cy[j].r+1;
				c[cnt1].s.push_back(j);
			}
			c[cnt1].nc=ce(c[cnt1].cy[c[cnt1].cys].r+1,c[cnt1].dj);
			cout<<c[cnt1].nc<<" "<<c[cnt1].dj<<endl;
		}
		else if(op==2)
		{
			string lx,x;
			cin>>lx>>c[0].cy[++cnt].na;
			c[0].cys++;
			c[0].cy[cnt].lx=q[lx];
			if(las==0)
			{
				c[0].cy[cnt].l=0;
			}
			else c[0].cy[cnt].l=ce(las,c[c[0].cy[cnt].lx].dj);
			c[0].ys[c[0].cy[cnt].na]=cnt;
			c[0].cy[cnt].r=c[0].cy[cnt].l+c[c[0].cy[cnt].lx].nc-1;
			c[0].dj=max(c[0].dj,c[c[0].cy[cnt].lx].dj);
			las=c[0].cy[cnt].r+1;
			c[0].s.push_back(cnt);
			cout<<c[0].cy[cnt].l<<endl;
		}
		else if(op==3)
		{
			string s;
			cin>>s;
			cout<<fi(0,s)<<endl;
		}
		else if(op==4)
		{
			int x;
			cin>>x;
			cout<<fin(0,x)<<endl;
		}
	}
	return 0;
}

T4:

很明显答案具有单调性,于是尝试用二分解决这道题,我们处理出每个点最迟要在什么时候种。然后贪心的先种时间早的树,从往上找直到找到根节点或者一个已经种了的树,然后一直种下来,最后判断一下所有树能不能长成就行了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],c[maxn],b[maxn];
int f[maxn],fa[maxn],maxx;
int d[maxn],e[maxn];
vector<int>v[maxn];
int x=1;
inline void getf(int u)
{
	if(f[u])return ;
	getf(fa[u]);
	f[u]=++x;
}
inline void dfs(int u,int fath)
{
	fa[u]=fath;
	for(int i=0;i<v[u].size();i++)
	{
		if(v[u][i]==fath)continue;
		dfs(v[u][i],u);
	}
}
int p[maxn];
bool cmp(int x,int y)
{
	return e[x]<e[y];
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld",a+i,b+i,c+i);
	}
	for(int i=1;i<n;i++)
	{
		int u,y;
		scanf("%lld%lld",&u,&y);
		v[u].push_back(y);
		v[y].push_back(u);
	}
	dfs(1,1);
	int l=n+1,r=1e9;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			if(c[i]<0)
			{
				int o=min((b[i]-1)/-c[i],mid);
				int L=1,R=mid;
				while(L<=R)
				{
					int Mid=(L+R)>>1;
					__int128 sum=0;
					if(Mid<=o)
					{
						sum=b[i]+b[i];
						sum+=o*c[i];
						sum+=Mid*c[i];
						sum*=(o-Mid+1);
						sum/=2;
						sum+=mid-o;
					}
					else sum+=mid-Mid+1;
					if(sum>=a[i])L=Mid+1;
					else R=Mid-1;
				}
				e[i]=R;
			}
			else
			{
				int L=1,R=mid;
				while(L<=R)
				{
					int Mid=(L+R)>>1;
					__int128 sum=0;
					sum=Mid+mid;
					sum*=c[i];
					sum+=b[i]+b[i];
					sum*=(mid-Mid+1);
					sum/=2;
					if(sum>=a[i])L=Mid+1;
					else R=Mid-1;
				}
				e[i]=R;
			}
			if(e[i]<n)p[++cnt]=i;
		}
		f[1]=1;
		sort(p+1,p+cnt+1,cmp);
		x=1;
		for(int i=1;i<=cnt;i++)
		{
			getf(p[i]);
		}
		bool o=false;
		for(int i=1;i<=n;i++)
		{
			if(f[i]>e[i])
			{
				o=true;
			}
			f[i]=0;
		}
		if(o)l=mid+1;
		else r=mid-1;
	}
	cout<<l;
	return 0;
} 
posted @ 2023-11-04 16:32  Shxt_Plus  阅读(24)  评论(0)    收藏  举报