985 字
5 分钟
Codeforces_Global_30
2025-12-19
无标签

Codeforces Global Round 30 (Div. 1 + Div. 2)#

链接 https://codeforces.com/contest/2164

4题,Ranking 1.5k

A题#

只需要判断数组是否存在k,或者存在<k,也存在>k,做一下判断

void solve()
{
int n;cin>>n;
ve<int>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
int x;cin>>x;
int op=0,id1=0,id2=0;
for(int i=1;i<=n;i++)
{
if(a[i]==x) op=1;
if(a[i]>x) id1=1;
if(a[i]<x) id2=1;
}
if(op||(id1&&id2))
{
cout<<"Yes\n";
}
else cout<<"No\n";
}

B题#

猜结论,猜测,出现的概率很大,对第i个位置,暴力向后跑100次

void solve()
{
int n;cin>>n;
ve<int>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
ve<int>f;
for(int i=1;i<=n;i++)
{
if(a[i]%2==0) f.push_back(a[i]);
}
if(f.size()>=2)
{
cout<<f[0]<<" "<<f[1]<<'\n';
return ;
}
else
{
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=min(n,i+100);j++)
{
if((a[j]%a[i])%2==0)
{
cout<<a[i]<<" "<<a[j]<<'\n';
return ;
}
}
}
}
cout<<"-1\n";
}

C题#

做法比较麻烦。肯定有更简单的做法

对数组a扔到小顶堆。然后数组,b,c做按照b升序的排序。 然后线段树维护数组c的区间最大值,以及最大值的下标,优先用数组a最小的,然后在数组b二分出a所能击败的位置id,查询区间[1,id]的最大值,更新小顶堆。随后修改击败位置的下标。

struct my_tree
{
// 区间和
#define kl (k<<1)
#define kr (k<<1|1)
struct node
{
int l, r, sum,id;
};
vector<node> tree;
my_tree(int x)
{
tree = vector<node>(x*4+10);
}
void pushup(node& kk, const node& kkl, const node& kkr)
{
if(kkl.sum>kkr.sum) kk.sum=kkl.sum,kk.id=kkl.id;
else kk.sum=kkr.sum,kk.id=kkr.id;
}
void pushup(int k)
{
pushup(tree[k], tree[kl], tree[kr]);
}
void build(int k, int l, int r, ve<int>& a)
{
tree[k]={l,r};
if (l == r)
{
tree[k].sum = a[l];
tree[k].id=l;
return;
}
int mid = (l + r) >> 1;
build(kl, l, mid, a);
build(kr, mid + 1, r, a);
pushup(k);
}
// 区间修改 // 区间替换
void update(int k, int l, int r, int val)
{
if (tree[k].l >= l && tree[k].r <= r)
{
tree[k].sum = val;
return;
}
int mid = (tree[k].l + tree[k].r) >> 1;
if (l <= mid) update(kl, l, r, val);
if (r > mid) update(kr, l, r, val);
pushup(k);
}
node query(int k, int l, int r)
{
if (l <= tree[k].l && tree[k].r <= r) return tree[k];
int mid = (tree[k].l + tree[k].r) >> 1;
node kkl = {tree[k].l, mid,-1,-1};
node kkr = {mid + 1, tree[k].r,-1,-1};
node kk = {tree[k].l, tree[k].r,-1,-1};
if (l <= mid) kkl = query(kl, l, r);
if (r > mid) kkr = query(kr, l, r);
pushup(kk, kkl, kkr);
return kk;
}
};
void solve()
{
int n,m;cin>>n>>m;
priority_queue<int,ve<int>,greater<>>pq;
for(int i=1;i<=n;i++)
{
int x;cin>>x;
pq.push(x);
}
ve<pii>a(m+1);
for(int i=1;i<=m;i++) cin>>a[i].first;
for(int i=1;i<=m;i++) cin>>a[i].second;
function<bool(pii,pii)>cmp=[&](pii x,pii y)
{
if(x.first!=y.first) return x.first<y.first;
return x.second>y.second;
};
sort(a.begin()+1,a.end(),cmp);
ve<int>b(m+1),c(m+1);
for(int i=1;i<=m;i++) b[i]=a[i].first,c[i]=a[i].second;
my_tree te(m+1);
te.build(1,1,m,c);//
int ans=0;
// for(int i=1;i<=m;i++) cout<<b[i]<<" \n"[i==m];
// for(int i=1;i<=m;i++) cout<<c[i]<<" \n"[i==m];
while(pq.size())
{
int x=pq.top();pq.pop();
int id=upper_bound(b.begin()+1,b.end(),x)-b.begin();
id--;
if(id<=0) continue;
auto [l,r,sum,ip]=te.query(1,1,id);
// cout<<1<<" "<<id<<' '<<sum<<" "<<ip<<'\n';
if(sum>0)
{
pq.push(max(x,sum));
}
if(sum>=0)
{
te.update(1,ip,ip,-1);
ans++;//
}
}
cout<<ans<<'\n';
}

一个更简单的做法,可以对c == 0的放到一个集合中,然后先去操作>0的,优先选择b小的,在现有的可重集合二分查找第一个可以击败b的,去更新 最后在对c==0的数组做操作。这样可以简化一下线段树的码量

void solve()
{
int n,m;cin>>n>>m;
ve<int>a(n+1),b(m+1),c(m+1);
int cnt0=0,cnt1=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
for(int i=1;i<=m;i++) cin>>c[i];
ve<int>id0,id1;
for(int i=1;i<=m;i++)
{
if(c[i]==0) id0.push_back(i);
else id1.push_back(i);
}
auto cmp=[&](int l,int r)->bool
{
if(b[l]!=b[r]) return b[l]<b[r];
return l<r;
};
sort(id0.begin(),id0.end(),cmp);
sort(id1.begin(),id1.end(),cmp);
multiset<int>se;
for(int i=1;i<=n;i++) se.insert(a[i]);
int ans=0;
for(int k=0;k<id1.size();k++)
{
int i=id1[k];//优先拿出较小的b。
auto it=se.lower_bound(b[i]);//找第一个大于等于他的a
if(it==se.end()) break;
int x=*it;//
se.erase(it);
x=max(x,c[i]);
se.insert(x);
ans++;
}
// for(auto x:se) cout<<x<<" ";
// cout<<'\n';
// cout<<ans<<'\n';
for(auto i:id0)
{
auto it=se.lower_bound(b[i]);
if(it==se.end()) break;
se.erase(it);
ans++;
}
cout<<ans<<'\n';
}

D题#

s[i]=s[i-1] || s[i]=s[i] 得到i位置一定由<i前面的位置更新,所以优先从后向前去考虑满足第i个位置前提下,如何满足<i的位置,如果第下标l<=i位置满足s[l]=t[i],这样i位置可以由l位置更新得到,此时记录[l,i]区间,对于i-1,的更新一定由<l的位置得到。

处理好每一个s[i]满足等于t[i]由l位置更新。这个操作可以nlogn的做法得到。整个s => s => t的操作次数就是maxi=1nmax_{i=1}^{n}(i-l)

void solve()
{
int n,k;cin>>n>>k;
ve<ve<int>>MP(27);
string s,t;cin>>s>>t;
for(int i=0;i<n;i++) MP[s[i]-'a'].push_back(i);
ve<pii>f;
int ls=n-1;
for(int i=n-1;i>=0;i--)
{
if(MP[t[i]-'a'].size()==0)
{
cout<<-1<<'\n';
return ;
}
int id=upper_bound(MP[t[i]-'a'].begin(),MP[t[i]-'a'].end(),min(i,ls))-MP[t[i]-'a'].begin();
id--;
if(id<0)
{
cout<<-1<<'\n';
return ;
}
id=MP[t[i]-'a'][id];//值
ls=min(ls,id);
if(id<i) f.push_back({id,i});//id--->i;
}
int cha=0;
for(int i=0;i<f.size();i++)
{
auto [l2,r2]=f[i];
// cout<<l2<<" "<<r2<<'\n';
cha=max(r2-l2,cha);
}
if(cha>k)
{
cout<<-1<<'\n';
return ;
}
cout<<cha<<'\n';
while(f.size())
{
ve<pii>h;
for(auto [l,r]:f)
{
// cout<<l<<" "<<l+1<<"\n";
// swap(s[l],s[l+1]);
s[l+1]=s[l];
l++;
if(l<r) h.push_back({l,r});
}
cout<<s<<'\n';
f=h;
// ends;
}
// ends;
}

E题#

没看,并查集,重构树,最近公共祖先

待施工

Codeforces_Global_30
https://fuwari.vercel.app/posts/codeforces_global_30/
作者
Lorem Ipsum
发布于
2025-12-19
许可协议
CC BY-NC-SA 4.0