CSP 2023 题解

  • 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;
    }
posted @   _XOFqwq  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示