AtCoder Beginner Contest 478

这一场读题爆炸了😅😅

A - Grapes

\(M\) 个数分给 \(N\) 个人。规则为从第一个人开始每人发一个,发到最后再从第一个开始。求每个人能获得的数量。

\(N\le 100,M\le 10^4\)

#include<bits/stdc++.h>
using namespace std;
int cnt[10001];
int main(){
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    int i=1;
    while(m--){
        cnt[i]++;i++;
        while(i==n+1) i=1;
    }
    for(int i=1;i<=n;++i){
        cout<<cnt[i]<<endl;
    }
}

B - Topping

有 \(N\) 个商品,第 \(i\) 个商品价格为 \(i\),价值为 \(w_i\)。在总价格不超过 \(V\) 的情况下选三件,使得总价值最高。

\(3\le N\le100,V\le 3N-3,W_i\le 10^6\)

这期鬼了。没看见image,写了个背包dp上去还寻思咋不对。

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long w[101];
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        cin>>w[i];
    }
    long long ans=0;
    for(int i=1;i<=n;++i){
        for(int j=i+1;j<=n;++j){
            for(int k=j+1;k<=n;++k){
                if(i+j+k<=m){
                    ans=max(ans,w[i]+w[j]+w[k]);
                }
            }
        }
    }
    cout<<ans<<endl;
}

C - Sort Subarray

给定一个长度为 \(N\) 的序列 \(a\) 和一个整数 \(K\),只能至多执行一次以下操作:

在 \([1,N-K+1]\) 中选择一个整数 \(i\),并将 \(a_i,a_{i+1}\dots,a_{i+K-1}\) 排序。问能否让整个数列有序。

\(K<N\le 2\times 10^5,1\le a_i\le N\)

md我以为啥玩意呢这么难。赛后发现题目里还有个image😅😅😅😅😅

但是其实有点好奇如果可以操作无限次怎么做。赛时我想的是判断每个数和它排好序以后所在的位置距离。

#include<bits/stdc++.h>
using namespace std;
int n,k;
int startpos[200001],endpos[200001];
int a[200001],b[200001];
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    for(int i=1;i<=n;++i){
        if(a[i]!=b[i]){
            sort(a+i,a+i+k);
            break;
        }
    }
    for(int i=1;i<=n;++i){
        if(a[i]!=b[i]){
            cout<<"No"<<endl;
            return 0;
        }
    }
    cout<<"Yes"<<endl;
}

D - Range Set Insertion Query

有 \(N\) 个不可重集合 \(s_1,s_2\dots s_N\),每次可执行操作 \((L,R,X)\),表示向 \(s_L,s_{L+1}\dots s_{R}\) 内都添加 \(X\)。最后求每个集合里有几个元素。

\(N,Q \le 2\times 10^5,X\le Q\)

实际上一开始想了一会数据结构,但是并没有什么数据结构能处理这个东西。

然后就考虑离线下来对 \(X_i\) 排序了。对 \(X_i\) 排序以后分组,这样实际上就变成了对若干个区间求并集。求并集是很简单的。\(X_i\) 内按左端点排序,然后扫过去就行了。关键是怎么才能进行数量统计。

想到的是前缀和,考虑维护差分数组,左端点对其加一,右端点减一即可。

#include<bits/stdc++.h>
using namespace std;
int n,q;
int diff[200010];
vector<pair<int,int>> g[200010];
int main(){
    cin>>n>>q;
    for(int i=1;i<=q;i++){
        int l,r,x;
        cin>>l>>r>>x;
        g[x].push_back({l,r});
    }
    for(int x=1;x<=q;x++){
        if(g[x].empty()) continue;
        sort(g[x].begin(),g[x].end());
        int cur=0;
        for(auto p:g[x]){
            int l=p.first,r=p.second;
            int nl=max(l,cur+1);
            if(nl<=r){
                diff[nl]++;
                diff[r+1]--;
            }
            cur=max(cur,r);
        }
    }
    int now=0;
    for(int i=1;i<=n;i++){
        now+=diff[i];
        cout<<now<<(i==n?"\n":" ");
    }
}

E - lt and le

判断是否存在一个数列 \(a\ (1\le a_i\le N)\) 满足给定条件:

给定的条件格式如下:\((t_i,u_i,v_i)\):如果 \(t_i=0\),那么要求 \(a_{u_i}\le a_{v_i}\);如果 \(t_i=0\),那么要求 \(a_{u_i}<a_{v_i}\)

如果存在,输出任意一个。

\(N,Q\le 2\times 10^5\)

666依旧读错题。要变成读错题仙人了。我以为image是 \(1\) 到 \(N\) 的排列。结果是可重的。

容易想到的是差分约束型建图。建图以后跑最长路约束。

不过这样的话存在环会对最长路无限次更新。所以需要对原图先缩点。缩点以后拓扑即可。

不存在数列的条件:同一个连通块内存在权值为 \(1\) 的内部边;存在一个点到根节点的最长路大于 \(N\)

#include<bits/stdc++.h>
using namespace std;
int n,Q;
struct edge{
    int to,w;
};
vector<edge>e[200001];
int w[200001];
int dfn[200001],low[200001],belong[200001];
int cnt,belongcnt;
stack<int>st;
bool instack[200001];
void tarjan(int id){
    dfn[id]=low[id]=++cnt;
    st.push(id);
    instack[id]=true;
    for(auto i:e[id]){
        //if(i.to==fa) continue;
        if(!dfn[i.to]){
            tarjan(i.to);
            low[id]=min(low[id],low[i.to]);
        }
        else if(instack[i.to]){
            low[id]=min(low[id],dfn[i.to]);
        }
    }
    if(low[id]==dfn[id]){
        int now;
        belongcnt++;
        //cout<<"same ["<<belongcnt<<"] ";
        do{
            now=st.top();
            st.pop();
            //cout<<now<<" ";
            belong[now]=belongcnt;
            instack[now]=false;
        }while(now!=id);
        //cout<<endl;
    }
}
vector<edge>ne[200001];
int nw[200001];
int indeg[200001];
void build(){
    for(int i=1;i<=n;++i){
        nw[belong[i]]+=w[i];
        for(auto j:e[i]){
            if(belong[i]!=belong[j.to]){
                ne[belong[i]].push_back({belong[j.to],j.w});
                indeg[belong[j.to]]++;
            }
            else if(j.w==1){
                cout<<"No"<<endl;
                exit(0);
            }
        }
    }
}
int dis[200001];
queue<int>q;
void topo(){
    for(int i=1;i<=belongcnt;++i){
        dis[i]=1;
        if(indeg[i]==0) q.push(i);
    }
    while(!q.empty()){
        int a=q.front();
        q.pop();
        for(auto i:ne[a]){
            if(dis[i.to]<dis[a]+i.w){
                dis[i.to]=dis[a]+i.w;
            }
            indeg[i.to]--;
            if(indeg[i.to]==0) q.push(i.to);
        }
    }
}
int main(){
    cin>>n>>Q;
    for(int i=1;i<=Q;++i){
        int t,x,y;
        cin>>t>>x>>y;
        if(t==0) e[x].push_back({y,0});
        else e[x].push_back({y,1});
    }
    for(int i=1;i<=n;++i){
        if(!dfn[i]) tarjan(i);
    }
    build();
    topo();
    for(int i=1;i<=belongcnt;++i){
        if(dis[i]>n){
            cout<<"No"<<endl;
            return 0;
        }
    }
    cout<<"Yes"<<endl;
    for(int i=1;i<=n;++i){
        cout<<dis[belong[i]]<<" ";
    }
    cout<<endl;
}

给定一棵由 \(N\) 个顶点 \(1,2,\ldots,N\) 构成的树 \(T\)。按照以下过程,可以得到一个 \((1,2,\ldots,N)\) 的排列 \(P\),记为 \(f(T)\)。

  • 初始时,令序列 \(P\) 为空序列,集合 \(S={1}\)。

  • 重复以下操作:

    • 令 \(x\) 为 \(S\) 中的最小值。从 \(S\) 中删除 \(x\),并将 \(x\) 添加到 \(P\) 的末尾。
    • 将顶点 \(x\) 相邻的顶点中尚未出现在 \(P\) 中的顶点都加入 \(S\)。

给定一个 \(1\) 到 \(N\) 的排列 \(Q\),求满足 \(f(T)=Q\) 的树 \(T\) 的数量,并对 \(998244353\) 取模。

\(N\le2\times 10^5\)

可以考虑对序列中的数,列举它的父节点是哪一个。

显然父节点在序列中的位置应该比这个节点靠前。还有一个性质:即序列中父节点与该节点之间,不可能存在比该节点更大的节点。证明:否则更大的节点一定不早于该节点进入集合,所以不存在该节点排在后面的情况。

所以只需要维护一下序列前面最后一个比当前数大的是哪个。单调栈维护即可。

其实写起来不难,但是推这个性质挺难的。

#include<bits/stdc++.h>
using namespace std;
struct a{
    int num,pos;
};
stack<a>st;
int n;
int q[200001];
long long ans=1;
const long long mod=998244353;
int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>q[i];
    }
    for(int i=1;i<=n;++i){
        while(!st.empty() and st.top().num<q[i]) st.pop();
        if(st.empty()){
            if(i!=1) ans=ans*(i-1)%mod;
        }
        else{
            ans=ans*(i-st.top().pos)%mod;
        }
        st.push({q[i],i});
    }
    cout<<ans<<endl;
}
posted @ 2026-10-11 18:18  HaneDaniko  阅读(24)  评论(1)    收藏  举报