CSP 2023 题解

Posted on 2024-03-02 17:03  _XOFqwq  阅读(12)  评论(0编辑  收藏  举报
  • PJ

    • T1

    首先容易发现每次都会取走 \(\dfrac{n-1}{3}+1\) 个苹果,因此 \(\lceil \dfrac{n}{\dfrac{n-1}{3}+1} \rceil\) 即为总天数。

    然后我们可以注意到当第一次出现 \(n-1 \bmod 3=0\) 时第 \(n\) 个苹果会被取到,因此我们在不断取苹果的过程中判断并记录即可。

    int n,d=1,dd;
    cin>>n;
    while(n>0){
    	if((n-1)%3==0&&!dd) dd=d;
    	n=n-((n-1)/3+1);
    	d++;
    }
    cout<<d-1<<' '<<dd;
    
    • T2

    考虑到每个站点都要从它前面最小的站点转移而来,于是用 \(mnv\)\(now[]\) 分别记录 前缀最小价格在最小价格所在站点时车剩余的油量,然后按题意模拟即可。

    #define dd double
    int n,d,pre=1,mnv=1e9;
     int ans[100031];
     dd now[100031];
     int v[100031],sum[100031];
     int a[100031];
     
    cin>>n>>d;
     for(int i=1;i<n;i++) cin>>v[i],sum[i+1]=sum[i]+v[i];
     for(int i=1;i<=n;i++) cin>>a[i];
     for(int i=2;i<=n;i++){
     	if(a[i-1]<mnv) pre=i-1,mnv=a[i-1];
     	ans[i]+=ans[pre];
     	dd oil=(dd)(sum[i]-sum[pre])/(dd)(d);
     	if(now[pre]>oil) now[pre]-=oil;
     	else{
     		ans[i]+=ceil(oil-now[pre])*a[pre];
     		now[i]=ceil(oil-now[pre])-oil;
     	}
     	now[i]+=now[pre];
     }
     cout<<ans[n];
    
    • T3

    \(\sqrt{\Delta}\) 转化为 \(c \sqrt{d}\) 的形式,根据 \(\sqrt{d}\) 是否为有理数进行讨论:

    • 若其为有理数(\(d=0\)\(d=1\)),约分后输出 \(\dfrac{-b+ad}{2a}\)

    • 否则,将其分解为 \(\dfrac{-b}{2a} + \dfrac{cd}{2a}\) 的形式,约分后输出即可(后者仅需将 \(c\)\(2a\) 约分)。

    int t,m;
    cin>>t>>m;
    void solve(){
    	int a,b,c; cin>>a>>b>>c;
    	if(a<0) a=-a,b=-b,c=-c;
    	int d=b*b-4*a*c,k=1;
    	if(d<0) cout<<"NO";
    	else{
    		for(int i=2;i*i<=d;i++)
    			while(d%(i*i)==0)
    				k*=i,d/=(i*i);
    		if(d==0||d==1){
    			int t=(__gcd(2*a,-b+k*d));
    			cout<<(-b+k*d)/t;
    			if(2*a/t!=1) cout<<"/"<<2*a/t;
    		}
    		else{
    			int t=(__gcd(2*a,-b));
    			if((-b)/t!=0){
    				cout<<(-b)/t;
    				if(2*a/t!=1) cout<<"/"<<2*a/t;
    				cout<<"+";
    			}
    	
    			t=(__gcd(2*a,k));
    			if(k/t!=1) cout<<k/t<<"*";
    			cout<<"sqrt("<<d<<")";
    			if(2*a/t!=1) cout<<"/"<<2*a/t;
    		}
    	}
    	cout<<"\n";
    }
    
    • T4

    我们考虑 \(dp\) 的思想,令 \(dis_{i,j}\) 表示在节点 \(i\) 时刻 \(j\)(模 \(k\) 意义下),然后对图进行一遍 \(dijkstra\) 即可。

    答案为 \(dis_{n,0}\)

    #include<bits/stdc++.h>
     using namespace std;
    
     int n,m,k;
     struct NodeMessage{
     	int u,p,d;
     	bool operator < (const NodeMessage &x) const {
         	return d>x.d;
     	}
     };
     int dis[200031][131];
     bool vis[200031][131];
     vector<pair<int,int> > G[200031];
     priority_queue<NodeMessage> q;
    
     signed main(){
     	cin>>n>>m>>k;
     	for(int i=1,u,v,w;i<=m;i++){
     		cin>>u>>v>>w;
     		G[u].push_back({v,w});
     	}
     	memset(dis,0x3f,sizeof(dis));
     	q.push({1,0,dis[1][0]=0});
     	while(!q.empty()){
     		int nu=q.top().u,np=q.top().p; q.pop();
     		if(vis[nu][np]) continue; vis[nu][np]=1;
     		for(auto i:G[nu]){
     			int nv=i.first,nw=i.second;
     			int dd=dis[nu][np],pp=(np+1)%k;
     			if(dd<nw) dd+=ceil((double)(nw-dd)/(double)(k))*k;
     			if(dis[nv][pp]>dd+1) q.push({nv,pp,dis[nv][pp]=dd+1});
     		}
     	}
     	cout<<(dis[n][0]==0x3f3f3f3f?-1:dis[n][0]);
     	return 0;
     }
    
  • TG

    • T1

    枚举所有状态 check 即可。

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,ans;
    int r[31][5];
    
    bool check(int a,int b,int c,int d,int e){
    	int s[5];
    	s[0]=a,s[1]=b,s[2]=c,s[3]=d,s[4]=e;
    	for(int i=1;i<=n;i++){
    		int ss=0;
    		for(int j=0;j<5;j++)
    			if(r[i][j]!=s[j]) ss++;
    		if(ss>2||ss==0) return 0;
    		else if(ss==2){
    			int p1=-1,p2=-1;
    			for(int j=0;j<5;j++){
    				if(r[i][j]!=s[j]&&p1==-1) p1=j;
    				else if(r[i][j]!=s[j]&&p1!=-1){
    					p2=j;
    					break;
    				}
    			}
    			if(abs(p1-p2)!=1) return 0;
    			int x=(s[p1]-r[i][p1]>=0?s[p1]-r[i][p1]:s[p1]+10-r[i][p1]);
    			int y=(s[p2]-r[i][p2]>=0?s[p2]-r[i][p2]:s[p2]+10-r[i][p2]);
    			if(x!=y) return 0;
    		}
    	}
    	return 1;
    }
    
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<5;j++)
    			cin>>r[i][j];
    	for(int a=0;a<=9;a++)
    		for(int b=0;b<=9;b++)
    			for(int c=0;c<=9;c++)
    				for(int d=0;d<=9;d++)
    					for(int e=0;e<=9;e++)
    						if(check(a,b,c,d,e))
    							ans++;
    	cout<<ans;
    	return 0;
    } 
    
    
    • T2

    考虑一种 \(O(n^2)\) 的做法:拿一个栈,不断往后扩展,遇到和栈顶相同就弹栈,统计数量即可。

    于是正解就是将所有栈的状态表示成一个 hash 值,扔到 map 里看有几个和它相同的即可累加。

    #include<bits/stdc++.h>
    #define int long long
    #define ull unsigned long long
    using namespace std;
    
    int n,ans,hsh;
    map<int,int> cnt;
    stack<int> s;
    ull p[2000031];
    string t;
    
    signed main(){
    	cin>>n>>t;
    	p[0]=1;
    	for(int i=1;i<=2000010;i++) p[i]=p[i-1]*13331;
    	cnt[0]++;
    	for(int i=0;i<n;i++){
    		if(!s.empty()&&s.top()==t[i])
    			hsh-=s.top()*p[s.size()],s.pop();
    		else
    			s.push(t[i]),hsh+=s.top()*p[s.size()];
    		ans+=cnt[hsh],cnt[hsh]++;
    	}
    	cout<<ans;
    	return 0;
    }
    
    • T3

    大模拟。实在不想讲了。放个tj链接罢

    #include<bits/stdc++.h>
    #define int long long
    #define pii pair<int,int>
    using namespace std;
    
    int n;
    const int MAXN=331;
    string New_Type_Meb_Type[MAXN],New_Type_Meb_Name[MAXN];
    
    struct Type{
    	string Name;
    	int M_Size,M_Align;
    	vector<Type*> M_Type;
    	vector<string> M_Name;
    	void cls(){
    		Name.clear(),M_Size=M_Align=0;
    		M_Type.clear(),M_Name.clear();
    	}
    };
    Type Def_Type[MAXN];
    int Type_Num;
    map<string,Type*> Name_to_Type;
    
    struct Elem{
    	Type Elem_Type;
    	string Name;
    	int Addr;
    	void cls(){
    		Elem_Type.cls();
    		Name.clear(),Addr=0;
    	}
    };
    Elem Def_Elem[MAXN];
    int Elem_Num;
    map<string,Elem*> Name_to_Elem;
    
    int Addr_Pos;
    map<pii,Elem*> Addr_to_Elem;
    
    void Init(){
    	Def_Type[++Type_Num].Name="byte";
    	Def_Type[Type_Num].M_Size=Def_Type[Type_Num].M_Align=1;
    	Name_to_Type["byte"]=&Def_Type[Type_Num];
    
    	Def_Type[++Type_Num].Name="short";
    	Def_Type[Type_Num].M_Size=Def_Type[Type_Num].M_Align=2;
    	Name_to_Type["short"]=&Def_Type[Type_Num];
    
    	Def_Type[++Type_Num].Name="int";
    	Def_Type[Type_Num].M_Size=Def_Type[Type_Num].M_Align=4;
    	Name_to_Type["int"]=&Def_Type[Type_Num];
    
    	Def_Type[++Type_Num].Name="long";
    	Def_Type[Type_Num].M_Size=Def_Type[Type_Num].M_Align=8;
    	Name_to_Type["long"]=&Def_Type[Type_Num];
    }
    
    void Create_New_Type(string N_Type_Name,int N_Type_Num,string *N_Type_Meb_Type,string *N_Type_Meb_Name){
    	Type New_Type;
    	New_Type.cls();
    	New_Type.Name=N_Type_Name;
    	for(int i=1;i<=N_Type_Num;i++){
    		New_Type.M_Type.push_back(Name_to_Type[N_Type_Meb_Type[i]]);
    		New_Type.M_Name.push_back(N_Type_Meb_Name[i]);
    	}
    
    	int N_Pos=0;
    	vector<Type*>::iterator N_It=New_Type.M_Type.begin();
    	for(;N_It!=New_Type.M_Type.end();N_It++){
    		int T_Size=(*N_It)->M_Size,T_Align=(*N_It)->M_Align;
    		New_Type.M_Align=max(New_Type.M_Align,T_Align);
    		if(N_Pos%T_Align) N_Pos=(N_Pos/T_Align+1)*T_Align;
    		N_Pos+=T_Size;
    	}
    	if(N_Pos%New_Type.M_Align) N_Pos=(N_Pos/New_Type.M_Align+1)*New_Type.M_Align;
    	New_Type.M_Size=N_Pos;
    	Def_Type[++Type_Num]=New_Type;
    	Name_to_Type[N_Type_Name]=&Def_Type[Type_Num];
    }
    
    void Create_New_Elem(string N_Elem_Type,string N_Elem_Name){
    	Elem New_Elem;
    	New_Elem.cls();
    	int T_Size=Name_to_Type[N_Elem_Type]->M_Size;
    	int T_Align=Name_to_Type[N_Elem_Type]->M_Align;
    	if(Addr_Pos%T_Align) Addr_Pos=(Addr_Pos/T_Align+1)*T_Align;
    	New_Elem.Addr=Addr_Pos;
    	New_Elem.Elem_Type=*Name_to_Type[N_Elem_Type];
    	New_Elem.Name=N_Elem_Name;
    	Def_Elem[++Elem_Num]=New_Elem;
    	Name_to_Elem[N_Elem_Name]=&Def_Elem[Elem_Num];
    	Addr_to_Elem[{Addr_Pos,Addr_Pos+T_Size-1}]=&Def_Elem[Elem_Num];
    	Addr_Pos+=T_Size;
    }
    
    int Visit_Elem(string N_Elem_Name){
    	queue<string> Que_Elem_Name;
    	string Tool; Tool.clear();
    	for(int i=0;i<N_Elem_Name.size();i++){
    		if(N_Elem_Name[i]=='.'){
    			Que_Elem_Name.push(Tool);
    			Tool.clear();
    		}
    		else Tool+=N_Elem_Name[i];
    	}
    	Que_Elem_Name.push(Tool);
    
    	int N_Pos=Name_to_Elem[Que_Elem_Name.front()]->Addr;
    	Type N_Type=Name_to_Elem[Que_Elem_Name.front()]->Elem_Type;
    	Que_Elem_Name.pop();
    	while(!Que_Elem_Name.empty()){
    		string N_Name=Que_Elem_Name.front(); Que_Elem_Name.pop();
    		vector<string>::iterator N_It1=N_Type.M_Name.begin();
    		vector<Type*>::iterator N_It2=N_Type.M_Type.begin();
    		for(;N_It1!=N_Type.M_Name.end();N_It1++,N_It2++){
    			if(N_Pos%(*N_It2)->M_Align) N_Pos=(N_Pos/(*N_It2)->M_Align+1)*(*N_It2)->M_Align;
    			if(*N_It1==N_Name){
    				N_Type=**N_It2;
    				break;
    			}
    			else N_Pos+=(*N_It2)->M_Size;
    		}
    	}
    	return N_Pos;
    }
    
    string Visit_Addr(int N_Goal_Addr){
    	if(N_Goal_Addr>=Addr_Pos) return "ERR";
    
    	Elem N_Elem;
    	map<pii,Elem*>::iterator N_It=Addr_to_Elem.begin();
    	for(;N_It!=Addr_to_Elem.end();N_It++){
    		if(N_Goal_Addr>=(*N_It).first.first&&N_Goal_Addr<=(*N_It).first.second){
    			N_Elem=*((*N_It).second);
    			break;
    		}
    	}
    
    	int N_Goal_Pos=N_Goal_Addr-N_Elem.Addr,N_Pos_ST=0,N_Pos_ED;
    	Type N_Type=N_Elem.Elem_Type;
    	string N_Elem_Name;
    	N_Elem_Name.clear();
    	N_Elem_Name+=N_Elem.Name;
    	while(!N_Type.M_Name.empty()){
    		vector<string>::iterator N_It1=N_Type.M_Name.begin();
    		vector<Type*>::iterator N_It2=N_Type.M_Type.begin();
    		for(;N_It2!=N_Type.M_Type.end();N_It1++,N_It2++){
    			N_Pos_ED=N_Pos_ST+(*N_It2)->M_Size;
    			if(N_Goal_Pos>=N_Pos_ST&&N_Goal_Pos<N_Pos_ED){
    				N_Elem_Name+='.'+(*N_It1);
    				N_Type=**N_It2;
    				break;
    			}
    			else{
    				if(N_It2+1==N_Type.M_Type.end()) return "ERR";
    				int T_Align=(*(N_It2+1))->M_Align;
    				if(N_Pos_ED%T_Align) N_Pos_ED=(N_Pos_ED/T_Align+1)*T_Align;
    				if(N_Goal_Pos<N_Pos_ED) return "ERR";
    				N_Pos_ST=N_Pos_ED;
    			}
    		}
    	}
    	if(N_Elem_Name=="") return "ERR";
    	return N_Elem_Name;
    }
    
    
    signed main(){
    	cin>>n;
    	Init();
    	for(;n;n--){
    		int op; cin>>op;
    		if(op==1){
    			string New_Type_Name;
    			int New_Type_Num;
    			cin>>New_Type_Name>>New_Type_Num;
    			for(int i=1;i<=New_Type_Num;i++)
    				cin>>New_Type_Meb_Type[i]>>New_Type_Meb_Name[i];
    			Create_New_Type(New_Type_Name,New_Type_Num,New_Type_Meb_Type,New_Type_Meb_Name);
    			cout<<Def_Type[Type_Num].M_Size<<' '<<Def_Type[Type_Num].M_Align<<'\n';
    		}
    		else if(op==2){
    			string New_Elem_Type,New_Elem_Name;
    			cin>>New_Elem_Type>>New_Elem_Name;
    			Create_New_Elem(New_Elem_Type,New_Elem_Name);
    			cout<<Def_Elem[Elem_Num].Addr<<'\n';
    		}
    		else if(op==3){
    			string N_Elem_Name;
    			cin>>N_Elem_Name;
    			cout<<Visit_Elem(N_Elem_Name)<<'\n';
    		}
    		else{
    			int N_Goal_Addr;
    			cin>>N_Goal_Addr;
    			cout<<Visit_Addr(N_Goal_Addr)<<'\n';
    		}
    	}
    	return 0;
    }
    
    • T4

    题目一眼具有单调性,考虑二分天数 \(x\)

    check 中,我们可以通过再一次二分求出每个节点最晚的种树时间 \(t_i\)

    如何具体进行操作?

    我们有节点 \(u\) 在时刻 \([l,r]\) 中的树高度表达式:

    \[\sum_{i=l}^{r} \max(b_u+c_u \times i,1) \]

    对于 \(c_u \ge 0\),原式可化为

    \[b_u \times (r-l+1)+c_u \times \dfrac{(l+r) \times (r-l+1)}{2} \]

    \(c_u<0\),则我们令 \(t=\dfrac{1-b_u}{c_u}\)

    此时若 \(t<l\),则原式可化为

    \[r-l+1 \]

    \(t>r\),则原式可化为

    \[b_u \times (r-l+1)+c_u \times \dfrac{(l+r) \times (r-l+1)}{2} \]

    \(l \le t \le r\),则原式可化为

    \[b_u \times (t-l+1)+c_u \times \dfrac{(l+t) \times (t-l+1)}{2}+(r-t) \]

    (应该很好理解吧 qwq)

    于是,我们对于二分出的 \(mid\) 值,代入上式计算,若其 \(\ge\) 当前的 \(a_i\) 则可以往上二分,否则往下二分。

    当然若从第一时刻开始种树都无法满足当前要求就直接弃疗即可。

    求出 \(t_i\) 后,考虑一种贪心策略,即按 \(t_i\) 从小到大的顺序考虑每个节点,若未种树,则将根节点到它的路径上所有未种树的节点全种上树。贪心的正确性(似乎)是显然的。

    于是按照此贪心策略模拟种树即可。

    注意开 __int128

    #include<bits/stdc++.h>
    #define i128 __int128
    using namespace std;
    
    int n;
    long long a[100031];
    int b[100031],c[100031];
    vector<int> G[200031];
    bool vis[100031];
    int f[100031];
    i128 t[100031];
    int p[100031];
    int top,stk[100031];
    
    bool cmp(int x,int y){
    	return t[x]<t[y];
    }
    void dfs(int x,int fa){
    	f[x]=fa;
    	for(auto i:G[x]){
    		if(i==fa) continue;
    		dfs(i,x);
    	}
    }
    i128 calc(int x,i128 l,i128 r){
    	if(c[x]>=0) return (r-l+1)*b[x]+(l+r)*(r-l+1)/2*c[x];
    	i128 tt=(1-b[x])/c[x];
    	if(tt<l) return r-l+1;
    	else if(tt>r) return (r-l+1)*b[x]+(l+r)*(r-l+1)/2*c[x];
    	return (tt-l+1)*b[x]+(l+tt)*(tt-l+1)/2*c[x]+r-tt;
    }
    bool check(int x){
    	for(int i=1;i<=n;i++){
    		if(calc(i,1,x)<a[i]) return 0;
    		int l=0,r=n+1;
    		while(l+1<r){
    			int mid=(l+r)>>1;
    			if(calc(i,mid,x)>=a[i]) l=mid;
    			else r=mid;
    		}
    		t[i]=l,p[i]=i,vis[i]=0;
    	}
    	sort(p+1,p+n+1,cmp);
    	for(int i=1,tme=0;i<=n;i++){
    		int now=p[i],top=0;
    		for(;!vis[now];now=f[now]) vis[stk[++top]=now]=1;
    		for(;top;top--) if(t[stk[top]]<++tme) return 0;
    	}
    	return 1;
    }
    
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++) cin>>a[i]>>b[i]>>c[i];
    	for(int i=1,u,v;i<n;i++){
    		cin>>u>>v,
    		G[u].push_back(v),
    		G[v].push_back(u);
    	}
    	vis[0]=1;
    	dfs(1,0);
    	int l=n-1,r=1e9+1;
    	while(l+1<r){
    		int mid=(l+r)>>1;
    		if(check(mid)) r=mid;
    		else l=mid;
    	}
    	cout<<r;
    	return 0;
    }