2023 CSP-S

发布时间 2023-11-16 00:03:30作者: shirlybabyyy

P9752 [CSP-S 2023] 密码锁

 直接模拟统计就可以了,看每个状态有多少个转移的状态,就是输入的所有状态里面,把所以可能是由此转化来的++,最后循环所有的情况,如果能够转移的数量为n,那么就是结果++

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int maxn=1e5+10;
int n,ans,dp[11][11][11][11][11];
int main()
{
	//咋个就一开始没转过来呢
	cin>>n;
	for(int i=1;i<=n;i++){
		int a,b,c,d,e;
		cin>>a>>b>>c>>d>>e;
		for(int j=1;j<=9;j++){
			dp[(a+j)%10][b][c][d][e]++;
			dp[a][(b+j)%10][c][d][e]++;
			dp[a][b][(c+j)%10][d][e]++;
			dp[a][b][c][(d+j)%10][e]++;
			dp[a][b][c][d][(e+j)%10]++;
			dp[(a+j)%10][(b+j)%10][c][d][e]++;
			dp[a][(b+j)%10][(c+j)%10][d][e]++;
			dp[a][b][(c+j)%10][(d+j)%10][e]++;
			dp[a][b][c][(d+j)%10][(e+j)%10]++;
		}
	}
	for(int i=0;i<=9;i++){
		for(int j=0;j<=9;j++){
			for(int k=0;k<=9;k++){
				for(int t=0;t<=9;t++){
					for(int p=0;p<=9;p++){
						if(dp[i][j][k][t][p]==n) ans++;
					}
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

  

P9753 [CSP-S 2023] 消消乐

 类似于kmp思想,记录跳转的地方,让fi表示以i结尾的合法区间个数,fi=fj+1,那么(j+1,i)是一个合法的区间

要保证最大。那么(j+1,i)是以i结尾的最短合法区间,这里就是可以直接跳转

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int maxn=2e6+10;
//感觉有点kmp算法的思想
//记录跳转的地方,让fi表示以i结尾的合法区间个数,fi=fj+1,那么(j+1,i)是一个合法的区间
//要保证最大。那么(j+1,i)是以i结尾的最短合法区间
int dp[maxn] ,las[maxn];
int main()
{
	int n;cin>>n;
	string s;
	cin>>s;
	s='%'+s; //加个东西
	LL ans=0;
	for(int i=1;i<=n;i++){
		for(int j=i-1;j>0;j=las[j]-1){
			if(s[i]==s[j]){
				las[i]=j;break;
			}
		}
		if(las[i]) dp[i]=dp[las[i]-1]+1,ans+=dp[i];
	} 
	cout<<ans<<endl;
	return 0;
}

  

P9754 [CSP-S 2023] 结构体

 大模拟,很多细节,结构体、指针的运用,仔细就可以,没特别复杂

#include <bits/stdc++.h>

using namespace std;
#define LL long long
const int maxn=2e6+10;
//这个模拟比较难写,很多细节、结构体、指针很多应用
struct jiegou{
	string name,type; //别名和类型
	LL len,dp,py;
	//长度、对齐、相对父亲的偏移
	vector<jiegou *>son; //儿子(内涵的结构体)
	jiegou(){ py=len=dp=0;name=type="";
	} 
	jiegou(LL len,string tp){
		this->py=0;this->len=len;this->dp=len;
		this->type=tp;
	}
};
map<string,jiegou> mp; //初始化结构体
LL js(LL a,LL b){
	return a/b+int(a%b>0); //向上取整,计算偏移的时候需要 
} 
void build(string x,LL k){ //类型是x,里面有k个元素
	 jiegou tmp;
	 tmp.type=x;
 	 LL py=0;
 	 jiegou *las;
 	 for(LL i=1;i<=k;i++){
 	 	string tpe,bieming;
 	 	cin>>tpe>>bieming;
 	 	jiegou *tmp1=new jiegou(mp[tpe]);
 	 	tmp1->name=bieming;
 	 	tmp.dp=max(tmp.dp,tmp1->dp);  //对齐长度取长度最大的元素
		if(i>1){
			tmp1->py=tmp1->dp*js(las->py+las->len,tmp1->dp); //计算这个元素的起始位置
			//就是这个元素的对齐长度乘以 上一个元素的偏移量加上长度除以这个元素的对齐长度并向上取整 
		} 
 	 	tmp.son.push_back(tmp1);
 	 	las=tmp1;
	  }
	  tmp.len=tmp.dp*js(las->len+las->py,tmp.dp); //整个结构体的长度
	  mp[x]=tmp;
	  cout<<tmp.len<<" "<<tmp.dp<<endl; //输出结构体的长度和对齐 
}
// 2  -> 加入内存
jiegou *tr=new jiegou(); //初识时没有初始化的结构体变量 
void add(string tpe,string name){
	jiegou *tmp=new jiegou(mp[tpe]);
	tmp->name=name;
	LL x,y,st=0;
	vector<jiegou *> vec=tr->son; //一开始是空的!!现在创建一个加入一个 
	if(vec.empty()){
		x=0;y=0;
	}
	else{
		x=(*--vec.end())->py;
		y=(*--vec.end())->len;
	}
	st=js(x+y,tmp->dp)*tmp->dp;  //偏移值 
	tmp->py=st; //这个元素的偏移 
	tr->son.push_back(tmp);
	cout<<st<<endl;
} 
//3 -->直接暴力分割出来每一层索引的名字然后暴力一层一层跳就可以了
LL findd(string x){
	vector<string> vec;
	LL las=0,len=x.length();
	for(LL i=1;i<=len;i++){
		if(x[i-1]=='.') vec.push_back(x.substr(las,i-las-1)),las=i;
		//一层层放进去 
	}
	vec.push_back(x.substr(las,len-las));
	jiegou *now=tr;
	LL st=0,py;
	for(LL i=1;i<=vec.size();i++){
		py=st;
		for(int j=1;j<=now->son.size();j++){
			jiegou *k=now->son[j-1];
			py=st+k->py;
			if(k->name==vec[i-1]){
				st=py;
				now=k; //这个也在往里走,往里面搜索每一个元素 
				break;
			}
		}
		
	}
	return py; 
}
// 4  -->递归找一下,暴力一层层跳,递归存一下目前的总偏移量,如果子节点包含这个区间就往下找
string getb(jiegou *now,LL st,LL tar){
	string ans="";
	for(LL i=1;i<=now->son.size();i++){
		jiegou *k=now->son[i-1];
		if(st+(k->py)<=tar&&tar<st+(k->py)+(k->len)){ //在这个结构体里面 
			ans=k->name;
			if(k->son.size()) ans+="."+getb(k,st+(k->py),tar);
			break;
		}
	}
	if(ans.empty()) return "ERR";  //如果要返回err,实际上只有最后一层是err,因为有可能在一个结构体里面,知只是没有被占据 
	else return ans;
} 
int main()
{
	LL n;
	cin>>n;
	mp["long"]=jiegou(8ll,"long");
	mp["int"]=jiegou(4ll,"int");
	mp["short"]=jiegou(2ll,"short");
	mp["byte"]=jiegou(1ll,"byte");
	for(LL i=1;i<=n;i++){
		int opt;
		cin>>opt;
		if(opt==1){
			string x;LL y;
			cin>>x>>y;
			build(x,y);
		}
		else if(opt==2){
			string x,y;
			cin>>x>>y;
			add(x,y);
		}
		else if(opt==3){
			string x;cin>>x;
			cout<<findd(x)<<endl;
		}
		else if(opt==4){
			LL adr;cin>>adr;
			string ans=getb(tr,0,adr);
			if(ans.find("ERR")!=-1) cout<<"ERR"<<endl;
			else cout<<ans<<endl;
		}
	}
	return 0;
}

  

P9755 [CSP-S 2023] 种树

 虽然知道有单调性 但是要写出二分还是比较难 --- 二层二分

https://www.luogu.com.cn/blog/979266/p9755-csp-s-2023-zhong-shu

写的比较好,首先因为bi+cix 这个是单调的所以可以从这里入手二分(区分什么时候取bi+cix什么时候取1),我们可以对于每一个地块i,求出最晚种树且到 x天时能到达 ai的天数ki

对于每个点i可以在1——ki之间任意一个时间种树,每个时间只能种一棵树,球有没有满足的方案
贪心做法:将ki从小到大排序,然后按照树的结构去判断是否有这样的方案(暴力枚举每个点到父亲的路上是否中了树)并且这个时间是否超过ki
开__int128

#include <bits/stdc++.h>
using namespace std;
typedef __int128 LL;
const int maxn=1e5+100;
const int INF=1e10;
//。虽然知道有单调性 但是要写出二分还是比较难  --- 二层二分 
//https://www.luogu.com.cn/blog/979266/p9755-csp-s-2023-zhong-shu  写的比较好
//首先因为bi+cix 这个是单调的所以可以从这里入手二分(区分什么时候取bi+cix什么时候取1),我们可以对于每一个地块i,求出最晚种树且到 x天时能到达 ai的天数ki 
//对于每个点i可以在1——ki之间任意一个时间种树,每个时间只能种一棵树,球有没有满足的方案
//贪心做法:将ki从小到大排序,然后按照树的结构去判断是否有这样的方案(暴力枚举每个点到父亲的路上是否中了树)并且这个时间是否超过ki 
//开__int128
inline LL read(){
    LL x=0,f=1;
    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();
    }
    return x*f;
}
inline void write(LL x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
} 
struct node{
	LL x,y; //表示最晚开始的天数,节点标号
	bool operator < (const node &yyy)const{
	return x<yyy.x;
	} 
}k[maxn];
LL n;
LL a[maxn],b[maxn],c[maxn],h[maxn],p[maxn],fa[maxn];
//h数组就是每个节点的最晚种树节点 
bool vis[maxn];
vector<LL> e[maxn]; //树 
void adde(LL u,LL v){
	e[u].push_back(v);
	e[v].push_back(u); 
} 
void dfs(LL u,LL f){
	for(int i=0;i<e[u].size();i++){
		LL op=e[u][i];
		if(op==f) continue;
		fa[op]=u;
		dfs(op,u);
	}
}
LL js(LL i,LL l,LL r){ //计算第i课树,从l到r天的生长总长度
	LL t=r-l+1;
	if(h[i]<l) return t;
	if(h[i]>r) return t*b[i]+(((l+r)*t)/2)*c[i]; //只有前半部分 
	return (r-h[i])+(h[i]-l+1)*b[i]+(((l+h[i])*(h[i]-l+1))/2)*c[i];
	
}
bool check(LL x){
	for(int i=1;i<=n;i++){
		if(js(i,1,x)<a[i]) return 0; //如果最多x天,那么就算是从第一天开始也不行,说明肯定不是合法的方案 
		LL l=1,r=n; //看从第几个开始种树
		while(l<r){
			LL mid=(l+r+1)>>1;
			if(js(i,mid,x)>=a[i]) l=mid;
			else r=mid-1;
		}
		k[i]={l,i};
		p[i]=k[i].x; //最晚开始的时间   要多余记录这个,因为后面要比较 
	}
	memset(vis,0,sizeof(vis));
	sort(k+1,k+1+n); 
	vis[0]=1;
	LL ttt=0;
	for(int i=1;i<=n;i++){
		stack<LL> d;
		LL t=k[i].y;
		while(!vis[t]){
			d.push(t);
			vis[d.top()]=1;
			t=fa[t]; //往上走  因为肯定这些没有被种 
		}
		while(!d.empty()){
			++ttt;
			if(p[d.top()]<ttt) return 0;  //如果这个时间开始满足在最晚开始时间之前 
			d.pop();
		}
	}
	return 1;
}
int main(){
//	scanf("%lld",&n);
//	for(int i=1;i<=n;i++) scanf("%lld %lld %lld",&a[i],&b[i],&c[i]);
//	for(LL u,v,i=1;i<=n-1;i++){
//		scanf("%lld %lld",&u,&v);adde(u,v);
//	} 
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		b[i]=read();
		c[i]=read();
	}
	for(int u,v,i=1;i<n;i++){
		u=read(),v=read();
		adde(u,v);
	}
	for(int i=1;i<=n;i++){
		if(c[i]>=0) h[i]=INF;
		else h[i]=(1-b[i])/c[i];
	}
	dfs(1,1);
	LL l=n,r=1e9;  //l起始值为n 
	while(l<r){
		LL mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1; 
	}
	write(l);
	return 0;
}