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\)
这期鬼了。没看见
,写了个背包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我以为啥玩意呢这么难。赛后发现题目里还有个
😅😅😅😅😅
但是其实有点好奇如果可以操作无限次怎么做。赛时我想的是判断每个数和它排好序以后所在的位置距离。
#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依旧读错题。要变成读错题仙人了。我以为
是 \(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;
}
F - Min-First Search
给定一棵由 \(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;
}

浙公网安备 33010602011771号