2025.1.4 Hello 2025

比赛链接

Solved: 5/9

Upsolved: 6/9

Rank: 542


搞了个很长的大头,之后贴代码都不贴大头了。

#include<bits/stdc++.h>
using namespace std;
using ui=unsigned; using db=long double; using ll=long long; using ull=unsigned long long; using lll=__int128;
using pii=pair<int,int>; using pll=pair<ll,ll>;
template<class T1, class T2> istream &operator>>(istream &cin, pair<T1, T2> &a) { return cin>>a.first>>a.second; }
template<class T1> istream &operator>>(istream &cin, vector<T1> &a) { for (auto &x:a) cin>>x; return cin; }
template<class T1> istream &operator>>(istream &cin, valarray<T1> &a) { for (auto &x:a) cin>>x; return cin; }
template<class T1, class T2> bool cmin(T1 &x, const T2 &y) { if (y<x) { x=y; return 1; } return 0; }
template<class T1, class T2> bool cmax(T1 &x, const T2 &y) { if (x<y) { x=y; return 1; } return 0; }
istream &operator>>(istream &cin, lll &x) { x=0; static string s; cin>>s; for (char c:s) x=x*10+(c-'0'); return cin; }
ostream &operator<<(ostream &cout, lll x) { static char s[60]; int tp=1; s[0]='0'+(x%10); while (x/=10) s[tp++]='0'+(x%10); while (tp--) cout<<s[tp]; return cout; }
#define all(x) (x).begin(),(x).end()
const int mod = 998244353;
const ll inf=4e18;
ll qpow(ll x,ll y=mod-2,int m=mod){ll r=1;for(;y;y>>=1,x=x*x%m)if(y&1)r=r*x%m;return r;}
void solve(){
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin>>T;
//while(T--)cout<<(solve()?"YES":"NO")<<'\n';
while(T--)solve();
}

A. MEX Table

题意:将 0nm1 排成一个 n×m 矩阵,使行 mex 之和加列 mex 之和最大。

注意到 0 所在行列之外的 mex 均为 0,所以只需让 0 所在行或列 mex 取到 nm,另一个 mex 取 1

void solve(){
int n,m;
cin>>n>>m;
cout<<max(n,m)+1<<'\n';
}

B. Gorilla and the Exam

题意:给一个序列,任意修改其中 k 个数,使得出现的不同的数最少。

按出现次数从小到大排序,改成出现次数最多的数。

void solve(){
int n,k;
cin>>n>>k;
vector<int> a(n);
cin>>a;
if(n==k){cout<<"1\n";return;}
map<int,int> cnt;
for(int i=0;i<n;i++)cnt[a[i]]++;
int ans=0;
vector<int> b;
for(auto x:cnt)b.push_back(x.second);
sort(all(b));
int m=b.size(),res=m;
for(int i=0;i<m;i++){
if(k<b[i])break;
k-=b[i];
--res;
}
cout<<res<<'\n';
}

C. Trip to the Olympiad

题意:给定区间 [l,r],从中选取三个不同的整数 a,b,c,使 (ab)+(bc)+(ca) 最大。

尽量使每一位都同时出现 0 和 1。直接令 a=l,c=r,从高到低考虑 b 的二进制位:

  • lr 的某位相同,且之前未出现过不同,则 b 也只能与 lr 相同;

  • lr 的某位相同,且之前出现过不同,则 blr 不同;

  • lr 的某位不同,且之前未出现过不同,则一定是 l 的这一位是 0r 的这一位是 1。此时找到这一位之后最高的相同位,若为 0 则表示 b 的这一位是 1,倒推回来当前位只能是 0(否则将大于 r),为 1 同理;

  • lr 的某位不同,且之前出现过不同,则取与第一次不同的位相反的数即可。

void solve(){
ll l,r;
cin>>l>>r;
ll a=l,c=r,b=0;
int o=__lg(r);
bool fl=1;
int t=0;
for(int i=o;i>=0;--i){
int x=l>>i&1,y=r>>i&1;
if(x==y){
if(fl)b|=x<<i;
else b|=(x^1)<<i;
}
else{
if(fl){
for(int j=i;j>=0;--j)
if((l>>j&1)==(r>>j&1)){
t=l>>j&1;
break;
}
b|=t<<i;
fl=0;
}
else b|=(t^1)<<i;
}
}
cout<<a<<' '<<b<<' '<<c<<'\n';
}

D. Gifts Order

题意:维护一个序列,支持单点修改,全局查询区间极差减区间长度的最大值。

首先最优区间一定左端点和右端点分别为最大和最小值。

若左端点为最大值,则答案是 alarr+l=(al+l)(ar+r);若右端点为最大值,则答案是 aralr+l=(arr)(all)

bi=aii,ci=ai+i,则答案是 max{maxl<rbrbl,maxl<rclcr}

对于单点修改,我们可以用线段树维护区间最大值、区间最小值和区间答案。

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
const int N=2e5+5;
int n,q,x,y,a[N],b[N],c[N];
int mxb[N*4],mnb[N*4],mxc[N*4],mnc[N*4],ansb[N*4],ansc[N*4];
void pushup(int x){
mxb[x]=max(mxb[lc],mxb[rc]), mnb[x]=min(mnb[lc],mnb[rc]);
mxc[x]=max(mxc[lc],mxc[rc]), mnc[x]=min(mnc[lc],mnc[rc]);
ansb[x]=max(max(ansb[lc],ansb[rc]),mxb[rc]-mnb[lc]);
ansc[x]=max(max(ansc[lc],ansc[rc]),mxc[lc]-mnc[rc]);
}
void build(int x,int l,int r){
if(l==r){
mxb[x]=mnb[x]=b[l];
mxc[x]=mnc[x]=c[l];
ansb[x]=ansc[x]=0;
return;
}
build(lc,l,mid);
build(rc,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int pos,int vb,int vc){
if(l==r){
mxb[x]=mnb[x]=vb;
mxc[x]=mnc[x]=vc;
ansb[x]=ansc[x]=0;
return;
}
if(pos<=mid)update(lc,l,mid,pos,vb,vc);
else update(rc,mid+1,r,pos,vb,vc);
pushup(x);
}
void solve(){
cin>>n>>q;
for(int i=1;i<=n;++i)cin>>a[i],b[i]=a[i]-i,c[i]=a[i]+i;
build(1,1,n);
cout<<max(ansb[1],ansc[1])<<'\n';
while(q--){
cin>>x>>y;
b[x]=y-x,c[x]=y+x;
update(1,1,n,x,b[x],c[x]);
cout<<max(ansb[1],ansc[1])<<'\n';
}
}

E. Another Exercise on Graphs

题意:给一张无向图,多次询问 xy 的所有路径中第 k 大边的最小值。n400

最小化第 k 大一类问题我们有经典的二分做法,将所有小于等于 x 的边视为 0,大于 x 的边视为 1,然后求最短路。
但直接套用到本题复杂度是 O(qmlogw) 甚至连 E1 都过不去。

答案仅可能是某条边的边权,我们考虑从小到大动态加边并维护“将大于当前边权视为 1 小于等于当前边权视为 0”的 dis 数组。

每加入一条边相等于将一条 1 边变为 0 边,这相当于对所有点对进行一次松弛,需要 O(n2) 更新。

本题 m=O(n2) 所以直接做是 O(n4) 仍然无法通过。但注意到有效松弛(会改变 dis 的松弛)只有 n1 次,因此同时维护一个并查集,只有加入不同集合的边时才进入松弛,复杂度就降到了 O(n3)

const int N=405,M=3e5+5;
int n,m,q,x,y,z,dis[N][N],ans[N][N][N];
struct node{int x,y,z;}buc[M],que[M];
int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void solve(){
cin>>n>>m>>q;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)if(i!=j){
dis[i][j]=inf;
for(int k=1;k<=n;++k)
ans[i][j][k]=inf;
}
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
dis[x][y]=dis[y][x]=1;
buc[i]={x,y,z};
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
cmin(dis[i][j],dis[i][k]+dis[k][j]);
sort(buc+1,buc+m+1,[](node a,node b){return a.z<b.z;});
for(int i=1;i<=q;++i)cin>>x>>y>>z,que[i]={x,y,z};
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1;i<=m;++i){
int x=buc[i].x,y=buc[i].y;
int xx=find(x),yy=find(y);
if(xx!=yy){
for(int j=1;j<=n;++j)
for(int k=1;k<=n;++k){
cmin(dis[j][k],min(dis[j][y]+dis[x][k],dis[j][x]+dis[y][k]));
if(dis[j][k]<=n&&ans[j][k][dis[j][k]+1]>=inf)ans[j][k][dis[j][k]+1]=buc[i].z;
}
fa[xx]=yy;
}
}
for(int i=1;i<=q;++i)cout<<ans[que[i].x][que[i].y][min(n,que[i].z)]<<' ';
cout<<'\n';
}
posted @   EssnSlaryt  阅读(370)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示